作者:蒸米
Ian Beer@google 发布了 CVE-2017-7047 Triple_Fetch 的 exp 和 writeup,chenliang@keenlab 也发表了一篇关于 Triple_Fetch 的分析,由于这个漏洞和 exp 有非常多的亮点,所以还剩很多可以深入挖掘的细节。因此,我们简单分析一下漏洞形成的原因,并具体介绍一下漏洞利用的细节,以及如何利用这个漏洞做到 iOS 10.3.2 上的沙盒逃逸。
因为 chenliang 对漏洞成因的分析非常详细,这里我就简单描述一下,因为使用 XPC 服务传输大块内存的话很影响效率,苹果为了减少传输时间,对大于 0x4000 的 OS_xpc_data
数据会通过 mach_vm_map
的方式映射这块内存,然后将这块数据的 send right 以 port 的方式发送到另一方。但这段内存的共享是基于共享物理页的方式,也就是说发送方和接收方会共享同一块内存,因此我们将数据发送以后再在发送端对数据进行修改,接收方的数据也会发生变化。
因此通过 race condition,可以让接收端得到不同的数据(接收端认为是相同的数据),如果接收端没有考虑到这一点的话就可能会出现漏洞。比如我们刚开始让接收端获取的字符串是 @”ABCD”(包括@和”),那么接收端会为这个字符串分配7个字节的空间。随后在进行字符串拷贝的时候,我们将字符串变为@"ABCDOVERFLOW_OVERFLOW_OVERFLOW"
,接收端会一直拷贝到遇到”符号为止,这样就造成了溢出。
Triple_Fetch 攻击所选择的函数是 CoreFoundation 里的 ___NSMS1() 函数,这个函数会对我们构造的恶意字符串进行多次读取操作,如果在读取的间隙快速对字符串进行三次修改,就会让函数读取到不同的字符串,让函数产生判断失误,从而造成溢出并让我们控制 pc,这也是为什么把这个漏洞称为 Triple_Fetch 的原因。下图就是攻击所使用的三组不同的字符串:
攻击所选择的 NSXPC 服务是 “com.apple.CoreAuthentication.daemon”
。对应的二进制文件是 /System/Library/Frameworks/LocalAuthentication.framework/Support/coreauthd
。原因是这个进程是 root 权限并且可以调用 processor_set_tasks() API 从而获取系统其他进程的 send right。下图是控制了pc后的crash report:
利用漏洞 Triple_Fetch 虽然可以控制 pc,但是还不能控制栈,所以需要先做 stack_pivot,好消息是 x0 寄存器指向的 xpc_uuid 对象是我们可以控制的:
因此我们可以利用 JOP 跳转到 _longjmp 函数作为来进行stack pivot,从而控制stack:
最终发送的用来做 JOP 的格式伪造的 xpc_uuid 对象如下:
控制了 stack 就可以很容易的写 rop 了。但是 beer 目标不仅仅是执行rop,它还希望获取目标进程的 task port 并且执行任意二进制文件,因此除了 exp,攻击端还用 machmsg 发送了 0x1000 个带有 send right 的 port 到目标进程中:
这些 port 的 machmsg 在内存中的位置和内容如下(msgh_id 都为 0x12344321):
随后,exp 采用 rop 的方法对这些 port 进行遍历并且发送回发送端:
随后,攻击端会接收 machmsg,如果获取到的 msgh_id 为 0x12344321 的消息,说明我们成果得到了目标进程的 task port:
得到了 task_port 后,sploit() 函数就结束了,开始进入 do_post_exploit()
。do_post_exploit()
也做了非常多的事情,首先是利用 coreauthd 的 task port 以及 processor_set_tasks()
获取所有进程的 task port。这是怎么做到的呢?
利用 coreauthd 的 task port 我们可以利用 mach_vm_* API 任意的修改 coreauthd 的内存以及寄存器,所以我们需要先开辟一段内存作为 stack,然后将 sp 指向这段内存,再将 pc 指向我们想要执行的函数地址就可以让目标进程执行任意的函数了,具体实现在 call_remote() 中:
随后我们控制 coreauthd 依次执行 task_get_special_port()
, processor_set_default()
, host_processor_set_priv()
,processor_set_tasks()
等函数,来获得所有进程的 task port 并返回给攻击端(具体实现在 get_task_ports())中。接着,攻击端会遍历这个列表并筛选出 amfid,launchd,installd,springboard 这四个进程的 task port。然后利用之前 patchamfid 的技巧,对
amfid 打补丁。最后再启动 debugserver。
其实这个 exp 不但可以执行 debugserver,还可以用来在沙盒外执行任意的二进制文件。只要把 pocs 文件夹下的 hello_world 二进制文件替换成你自己的想要执行的二进制文件,编译安装后,点击 ui 中的 exec bundle binary 即可:
具体怎么做到的呢?秘密在 spawn_bundle_binary()
函数中,先在目标进程中调用 chmod 将 bin 改为
0777,然后通过一系列的 posix_spawn API(类似fork())在目标进程中执行该 bin 文件。
沙盒外的代码执行提供了更多可以攻击内核的接口。并且可以读取甚至修改其他应用或者系统上的文件。比如,漏洞可以读取一些个人隐私数据(比如,短信,聊天记录和照片等)并发送到黑客的服务器上:
所以建议大家早日更新iOS系统到最新版本。
本文介绍了 beer 发现的通用 NSXPC 漏洞。另外,还分析了 iOS 用户态上,用 JOP 做 stack pivot 以及利用 ROP 做到任意代码执行的攻击技术。当然,这些漏洞只是做到了沙盒外的代码执行,想要控制内核还需要一个或两个XNU或者 IOKit 的漏洞才行,并且苹果已经修复了 yalu102 越狱用的 kpp 绕过方法,因此,即使有了Triple_Fetch 漏洞,离完成全部越狱还有很大一段距离。