原文地址:https://medium.com/@cji_/hunting-for-ios-kernel-symbols-e48a446bb00
上周,Google Project Zero的Ian Beer在Twitter上发文,他将通过task_for_pid_0或tfp0提供对内核内存的读、写访问,以帮助研究人员更深入研究iOS 11内核安全性。
这消息一出,可能人们更多的关注会放在新系统的越狱上面,但我想借此机会再研究一下移动安全。
Ian Beer昨日已将poc代码放在Project Zero的主页上Issue 1417: iOS/MacOS kernel double free due to IOSurfaceRootUserClient not respecting MIG ownership rules,包含有CVE-2016-7612
、CVE-2016-7633
两个漏洞。正当我准备好开始我的工作时,便遇到一个问题。Ian Beer放出来的poc代码中为几个设备添加了内核符号,但这几个设备我手头上并没有。运气比较好,他提供了有关如何找到符号的详细说明。
本文旨在介绍查找iOS符号的过程,因为我没有找到关于如何执行此操作的任何其他文档,因此我想为其他人(以及将来的我自己)记录此过程,希望对你添加对设备的支持也能有所帮助。
查找符号的第一步是获取目标iOS版本的kernelcache。我是通过访问ipsw.me并下载了我的iPad Mini 2的11.1.2固件,同样也可以下载任何已经在symbols.c
中定义了符号的设备。
在解压缩.ipsw文件之后,我下载一份joker工具。这是一个命令行工具,在阅读该工具的帮助页以后,设定好-j和-m选项并处理kernelcache文件,转储所有可用的符号。输出包含我需要的这些符号的地址:
KSYMBOL_OSARRAY_GET_META_CLASS KSYMBOL_IOUSERCLIENT_GET_META_CLASS KSYMBOL_IOUSERCLIENT_GET_TARGET_AND_TRAP_FOR_INDEX KSYMBOL_CSBLOB_GET_CD_HASH KSYMBOL_KALLOC_EXTERNAL KSYMBOL_KFREE KSYMBOL_OSSERIALIZER_SERIALIZE KSYMBOL_KPRINTF KSYMBOL_UUID_COPY
在用IDA Pro加载kernelcache文件之前,我们还需要解密kernelcache文件。详细的方法可以参考这里:getios10beta1kernelcache.sh
* open kernelcache in a hex editor and look for 0xFFCFFAEDFE, note the offset (435) * wget -q http://nah6.com/%7Eitsme/cvs-xdadevtools/iphone/tools/lzssdec.cpp * g++ -o lzssdec lzssdec.cpp * ./lzssdec -o 435 < kernelcache >kernelcache.dec # 435 is offset byte count to 0xFFCFFAEDFE header
首先,IDA加载解密好的kernelcache文件,kernelcache带有一些已知内核符号,我们可以通过反汇编的结果来大致了解一下iOS内核。
最先找到的是KSYMBOL_RET
,跳转到已知符号的地址,这是从_kalloc_external
函数返回的RET
指令。利用joker转储的符号地址信息很容易在IDA中跳转到对应的地址并且在同一个函数中找到这个RET
指令。
接下来是KSYMBOL_CPU_DATA_ENTRIES
,其中提示称数据段为0x6000。在IDA中,选择Jump to Segment
并转到_data段
的开头,将该地址加上0x6000
最终得到了我们所需的地址。
接下来的两个地址是KSYMBOL_EL1_HW_BP_INFINITE_LOOP
和KSYMBOL_SLEH_SYNC_EPILOG
,这是ksymbol
列表最后两个地址,具体可以看到Ian Beer利用代码里的symbols.h
。在IDA中打开了String窗口(Shift + F12)并搜索字符串,双击查看引用。
对于前者,向下阅读代码找到了这段switch case 49
语句,并且得到该地址。
对于后者,是字符串引用地址下面几个LDP
指令中的第一个。
最后一个就是KSYMBOL_X21_JOP_GADGET
,在看到所需的指令是MOV X21,X0
后,我在IDA中进行了搜索,以找到我的iPad设备的Gadget。
还剩下的5条内核符号地址是最棘手的,在IDA中搜索并没有找到什么有用的信息,所以我开始查看我找到的地址以及它们与已知地址的比较。再然后,通过对比symbols.h
里的符号地址之间的偏移差,我发现几个已经找到的地址之间偏移差基本上都能对应得上。最后,我比较了已知地址与未知地址之间的偏移差,例如,VALID_LINK_REGISTER和X21_JOP_GADGET仅相隔0x28个字节。
接下来,用IDA加载我设备的kernelcache并跳转到KNOWN_ADDRESS + my offset guess
,并开始在附近寻找相同的指令。
当找到所有所需内核符号地址,需在poc代码中再添加一个if路径来支持我的设备的。
添加一个prinf()
打印出信息,编译运行poc
最后漏洞在我的设备上成功触发。这是复现iOS设备内核漏洞的一般步骤,因为网上贴出来的poc、exp用到的内核符号地址一般与我们手头上的设备不一致,这样往往造成我们无法在本机上触发漏洞。