导语:在6月份的时候,文章中提到的两个“0day”漏洞被我们提交给了苹果,在iOS 11.4.1和iOS 12 beta中被修复了,但是在iOS 11.4以及之前版本中都可以被利用。
本文作者:蒸米,白小龙 @ 阿里安全
0x01 沙盒简介
苹果公司在macOS 10.5中把沙盒作为“SeatBelt”引入,它提供了MACF策略的第一个全面实现。在macOS上成功试用后,苹果公司又将沙盒机制应用于iOS 6中。随着新的系统的发布或新的威胁出现,沙盒的钩子数量一直在稳步的增长。如下是iOS/macOS每个版本中钩子的数量:
一开始,苹果的沙盒使用黑名单方式,这意味着苹果将已知的危险API整合在一起,并阻止它们,默认情况下允许所有其他人使用。随着苹果沙盒的发展,它采用了一种白名单的方式,拒绝所有的API,只允许苹果信任的安全接口。
在MacOS中,配置文件可见并存储在/System/Library/Sandbox/Profiles中。在iOS中,配置文件被硬编译到了/usr/libexec/sandboxd中。解码沙箱配置文件很困难,但我们可以遍历所有Mach服务根据返回值获取mach-lookup列表(例如,通过Jonathan Levin的sbtool)。
为了找到漏洞,我们需要反汇编和分析包含相关Mach服务的处理函数的二进制文件。/System/Library/ LaunchDaemons包含了大多数Mach服务的配置plist。在plist文件中,“ProgramArguments”字段显示了二进制文件的路径,“MachServices”显示了相关的mach服务。
0x02 iOS IPC: Mach, XPC and NSXPC
Mach消息包含类型化数据,可以包括端口权限和对大内存区域的引用。XPC消息建立在Mach消息之上,NSXPC消息建立在XPC消息之上。通过Mach消息,沙盒应用程序可以与未被沙盒的Mach(MIG)服务,XPC服务和NSXPC服务进行通信。
0x03 从老的漏洞到新的漏洞
bluetoothd的“com.apple.server.bluetooth”Mach服务中有132个函数(从0xFA300开始)。 蓝牙通过“com.apple.server.Bluetooth”与沙盒应用程序和其他非沙盒的进程(例如,SpringBoard)进行通信。进程可以使用BTSessionAttach为bluetoothd创建session_token,然后使用BTLocalDeviceAddCallbacks 为事件通知注册回调。
但是,Bluetoothd仅使用会话令牌来识别进程,这意味着我们可以使用沙盒应用程序通过会话令牌来劫持蓝牙和沙盒外的进程之间的通信(CVE-2018-4087)。
漏洞形成的原因是ses_token太容易被暴力破解了。它只有0x10000(0x0000 – 0xFFFF)个可能的值。Apple通过向每个会话添加user_id (= arc4random()) 来修复此问题,只有进程本身知道user_id,并且bluetoothd将检查map[ses_token] == user_id。
如前所述,user_id = arc4random()= [0x00000000-0xFFFFFFFF]。如果我们知道session_token,我们仍然可以通过user_id暴力劫持通信。 但这需要很长时间(约12小时)。如果没有user_id验证的话,还有没有其他的回调注册函数呢?答对了!0xFA365 BTAccessoryManagerAddCallbacks()!
但是,通过BTAccessoryManagerAddCallbacks()向bluetoothd发送消息后,没有任何反应! 最后,我发现了这个问题。 仅当iOS设备连接到新设备时才会触发回调事件,这意味着我们需要通过手动单击蓝牙设备来触发回调。
CallBacks 1(需要的时间很长),CallBacks 2(很难触发),再来一次CallBacks 3! 这次,我们又发现了一个可以注册回调函数的新函数,并且它很容易触发!
0xFA329 BTDiscoveryAgentCreate()可以为发现代理创建回调,然后我们可以使用0xFA32B BTDiscoveryAgentStartScan()来触发回调而无需手动点击!
0x04 从PC到ROP
我们的目标不仅是控制PC指针,还控制要控制整个进程。下一步是创建ROP链并对目标进程执行堆喷射。在这种情况下,我们使用MACH_MSGH_BITS_COMPLEX Mach消息以及MACH_MSG_OOL_DESCRIPTOR格式。如果我们发送消息并且没有接收消息,则ROP链将持续保留在目标的内存空间中。经过多次测试,我们可以找到一个MAGIC_ADDR 在 0x105400000这个地址。
我们可控制的寄存器:X3,X4,X5,X19,X20。 最后一个BR是X4。到目前为止,我们只能做BOP(JOP)。但是这样的话,我们很难控制程序流程。因此,我们需要一个stack pivot来控制堆栈并且从BOP 转换为 ROP。
在libsystem_platform.dylib中可以找到一个很棒的stack pivot gadget。如果我们可以控制x0,那么我们就可以控制sp。
0x05 从ROP到task port
端口为IPC提供了端点。消息可以发送到端口或从端口接收。端口可以包含权限,并且端口权限可以在消息中传递。一个进程最重要的端口是mach_task_self()。可以通过其任务端口来控制进程的内存和所有寄存器。
我们可以使用mach_vm_allocate(target_task_port,&remote_addr,remote_size,1)在远程进程中分配内存。mach_vm_write(target_task_port,remote_address,local_address,length)可用于将数据复制到远程进程中。 thread_create_running(target_task_port,ARM_THREAD_STATE64,&thread_state,stateCnt和thread_port)可用于在远程进程中创建新线程。因此,如果我们可以获得一个进程的任务端口。 我们可以通过mach msg轻松控制整个过程。
一些从Mach_portal学习到的技巧:
1.我们可以使用mach_port_insert_right(mach_task_self(),port,port,MACH_MSG_TYPE_MAKE_SEND)向端口插入发送权限。此类端口可以通过具有MACH_MSG_PORT_DESCRIPTOR类型的OOL消息发送。
2.在大多数情况下,mach_task_self()返回0x103,所以我们可以在不使用ROP的情况下使用0x103(调用mach_task_self())。
3.为了将任务端口发送到我们的pwn应用程序,我们需要知道我们的pwn应用程序的端口号。但是我们不能用launchd来帮助我们。幸运的是,端口号可以通过(0x103 + 0x100 * N)猜测。这就是我们向远程进程发送0x1000端口的原因(为了提高成功率)。
但是在iOS 11中,苹果加入了一个新的缓解机制用来控制沙盒中的app获取task port:
0x06 Plan B
虽然我们无法很容易的获取task port,但是我们可以利用下面的ROP gadget来调用任意函数:
使用这些ROP,我们可以打开更多的攻击面并进一步的攻击内核。
0x07 参考文献
1. MacOS and *OS Internals http://newosxbook.com/
2. Pangu 9 Internals https://www.blackhat.com/docs/us-16/materials/us-16-Wang-Pangu-9-Internals.pdf
3. triple_fetch https://bugs.chromium.org/p/project-zero/issues/detail?id=1247
4. https://blog.zimperium.com/cve-2018-4087-poc-escaping-sandbox-misleading-bluetoothd/
5. Mach portal https://bugs.chromium.org/p/project-zero/issues/detail?id=965
0x08 更新
在6月份的时候,文章中提到的两个“0day”漏洞被我们提交给了苹果,在iOS 11.4.1和iOS 12 beta中被修复了 (CVE-2018-4330和CVE-2018-4327)。但是在iOS 11.4以及之前版本中都可以被利用,请尽快升级您的iOS以避免潜在的攻击。