议题概要
苹果手机系统一直以来以安全性著称,苹果为保证iOS的安全性付出了大量心血。手机中的安全启动信任链(Secure Bootchain)是保证手机安全性至关重要的一个环节,一旦安全启动被攻破,后续的安全防护都将失去意义。
在这个议题中,Xerub介绍了苹果的安全启动机制,着重介绍了iBoot,并且回顾了在老版本的iOS系统中曾经存在的一个iBoot漏洞,分析了漏洞发现、利用及相应的修补方案。
作者介绍
Xerub是来自罗马尼亚的安全研究员,有着十二年信息安全领域从业经验。他的日常工作是恶意软件分析,还会研究一些模拟器。iOS破解是他的业余爱好,他主要关注其中用户态程序和iBoot的安全问题。
议题解析
iOS系统的安全性一直是iPhone的一大卖点,而一个安全的手机必须要有一个安全的启动信任链,苹果公司也花费了大量心血维护手机启动流程中的安全性,以保证手机能够在一个受信任的环境中工作。一旦安全启动信任链被破坏了,后续的各个安全功能将被割裂开来,无法再保证整个系统的完整性。
安全启动链包含若干个阶段,每个阶段都会加载、验证并运行下一个阶段的代码。自从iOS10以来,苹果已经放开了对固件中很多内容的限制,不再对固件的一些内容进行加密,但启动环节的程序仍然是加密状态,也就是说普通用户无法直接分析启动环节的程序逻辑。(然而苹果并没有保管好他们的代码,这部分程序的源代码已经泄露出来公开在网上了。)
iPhone会根据开机时的状态加载不同的功能模块,大致流程如下图:
最适合攻击bootchain的阶段是iBoot和iBec,因为他们是启动环节最复杂的阶段,需要支持USB请求、恢复模式和文件系统访问,而系统的复杂性和安全性是很难共存的,
这里我们重点关注iBoot的安全性。对于iOS9及以前的版本,在系统启动后,iBoot仍然残留在内存中,而且在32位的版本里这部分还映射在一片固定的物理内存里。对于iOS10及以上的64位系统呢,iBoot会被系统隐藏起来,但是我们可以从已越狱的手机中读取出来。
把特定的物理页面映射到虚拟地址就能从内核读到iBoot的数据,而且它没有地址空间随机化(ASLR),所有页面也都被映射成可读写执行(RWX),所有的内存分配都是完全可预测,所有的数据结构都在一个固定的地址上,它的地址空间布局如下:
我们比较关注的是iBoot里的文件系统驱动,因为它被用来加载内核,还有点复杂,而且还(曾经)是开源的。考虑到所有的内存布局都是可预测的,我们甚至可以大胆猜想:会不会递归栈溢出也是可以利用的漏洞?当程序调用递归程序时,必须要检查调用栈层数是否过深,否则会有可能用尽当前任务/线程的栈空间,导致内存破坏漏洞。尽管这种漏洞在现代操作系统环境中几乎不可利用,但是iBoot中稳定的内存布局的确给了我们一丝希望。
我们需要关注的并不一定是单个函数递归地调用自身,可以适当地放松对递归调用的要求,比如当我们发现某些函数之间会递归地相互调用,也是很值得研究的攻击场景。这里我们可以把程序中函数调用关系抽象成图(Call Graph),运用Tarjan算法求强连通分量。同一个强连通分量中的函数是相互可达的,这蕴含着递归调用发生的可能性。
利用Tarjan算法我们遇到了这样的一个函数调用场景:
- ReadExtent()
- memalign(64)
- ReadExtentsEntry() & ReadBTreeEntry()
- memalign(blockSize)
- ReadExtent()
iBoot中用到的HFS+文件系统中,所有文件和文件夹的信息都存在一个卷宗文件(Catalog File)中,里面用到了一个B-Tree结构。这个里面对每个版本的文件又记录了最多八个Extent。当这些Extent用完的时候,就会引用到一个外部的Extents Overflow File,里面又会用到一个B-Tree数据结构。我们可以看到这里出现了递归的调用,而且还可以观察到一旦进入这个递归状态,就无法停止终止无线递归的逻辑,直到触发不可恢复的内存破坏程序才会以崩溃告终。
因为无法控制递归调用的深度,我们不得不在无限递归过程中寻找机会完成攻击。结合之前分析的iBoot内存布局情况,我们的策略是在递归过程中修改内存分配器的元数据信息(the allocator meta-data),然后能控制在memalign()中分配到的内存,这样再从B-Tree读入数据时就能往任意可控的位置写入可控内容,最终能跳转到恶意代码,完全接管iBoot。大致的攻击路径如下图:
我们攻击的条件比较复杂,需要做不少准备工作。
- 确定每次递归消耗的栈空间
- 确定递归开始时的栈位置
- 构造B-Tree文件头,使得后续操作能够精准触发
- 检查是否有特殊数据结构在递归过程中被破坏
在我们没有调试设备的时候,只能借助模拟器(iloader)来分析iBoot的运行状态,我们需要
- 去掉所有硬件相关的操作指令
- 保证运行时的栈空间布局和真实环境一致
这个漏洞在iOS 8里被补掉了,苹果直接去掉了我们依赖的递归操作。实际上还可以做更多的安全防护,例如
- 在栈空间和其他数据段之间插入安全页面(Guard Pages)
- 堆地址随机化
- ASLR
总结
Xerub提到的iloader相关工具即将开源,在他的博客(https://xerub.github.io)上还有本议题更为详尽的技术解析。Bootchain作为苹果安全架构中最神秘的部分之一,还有很多未知的内容等待我们去探索。
审核人:Atoo 编辑:少爷