频道栏目
首页 > 安全 > 系统安全 > 正文

iOS安全之针对 mach_portal 的分析

2017-04-20 10:32:00           
收藏   我要投稿

一. 背景

iOS安全之针对 mach_portal 的分析。Google Project Zero的Ian Beer在12月中旬放出了在iOS 10.*上获取root shell的利用代码,意大利的Luca在此基础上添加了KPP绕过,实现了iOS 10.*的越狱。本文将结合mach_portal的源码对其利用的三个漏洞进行分析,并对每一个步骤进行说明。

mach_portal利用的漏洞都源于XNU内核对Mach Port的处理不当,相信这也是mach_portal名称的由来。XNU内核提供了多种进程间通信(IPC)的方法,Mach IPC就是其中的一种。Mach IPC基于消息传递的机制来实现进程间通信,关于Mach IPC的消息传递在众多书籍和文章中都有介绍,在此就不再赘述。我们这里介绍Mach Port。

Mach消息是在端口(Port)之间进行传递。一个端口只能有一个接收者,而可以同时有多个发送者。向一个端口发送消息,实际上是将消息放在一个消息队列中,直到消息能被接收者处理。

内核中有两个重要的结构,ipc_entry和ipc_object。ipc_entry是一个进程用于指向一个特定ipc_object的条目,存在于各个进程的ipc entry table中,各个进程间相互独立。

\

ipc_object就是ipc port或者ipc port set,实际上代表的就是一个消息队列或者一个内核对象(task,thread等等),Mach消息的传递就是通过ipc port的消息队列。ipc_object在内核中是全局的,一个ipc_object可以由不同进程间的ipc_entry同时引用。平常我们在编写代码时得到的Mach Port是一个32位无符号整型数,表示的是ipc_entry在ipc entry table中的索引值。经过MIG的转换后,在内核中,就可以从ipc_port得到实际的内核对象,例如convert_port_to_task等等。具体可以参考XNU源码和《Mac OS X Internals: A Systems Approach》,对于Mach IPC的相关数据结构有更为详细的说明。

\

二. 漏洞详情

mach_portal利用了三个漏洞,CVE-2016-7637、CVE-2016-7644、CVE-2016-7661。下面将对这三个漏洞的进行分析。

1. CVE-2016-7637

漏洞说明:内核对于ipc_entry的user reference处理不当,使得ipc_entry被释放后重用,导致特权port可能被替换成攻击者控制的port。(GPZ Issue 959)

漏洞分析:当一个进程接收到带有port的mach message时,函数调用的流程如下。

\

在ipc_right_copyout函数中,会将port right复制到当前task的ipc entry table中。ipc_entry的ie_bits的含义如下图。ie_bits的低16位为user reference,表示的当前ipc_entry的引用数量,最大值为0xFFFF。

\

当ipc_right_copyout处理 MACH_PORT_TYPE_SEND的port时,代码如下

\

可以看到,user references的值不会超过MACH_PORT_UREFS_MAX-1 = 0xFFFE。考虑这样一种场景,当前进程收到一个ool ports descriptor消息,当这条ool ports descriptor消息因为不符合接收进程的标准而被销毁时,以mach_msg_server为例,会调用mach_msg_destroy释放消息中带有的所有port right,如图。

\

ool ports descriptor被销毁时,会调用mach_msg_destroy_port释放每一个port right,更下层的函数会调用ipc_right_dealloc减少port对应的ipc_entry的一个引用(urefs)。当urefs等于0时,这个ipc_entry就会被释放到free list中,表示当前entry已经处于空闲状态,可以被申请用于指向另一个ipc_object。

\

如果消息中带有同一个port的0x10000个descriptor,那么在处理这个消息时就会使得当前这个port对应的ipc_entry的user reference到达上限0xFFFE。当被销毁时,这个ipc_entry就会被释放0x10000次,进而进入free list。然而,用户空间并不知道这个ipc_entry已经被释放,因为用户空间的进程保留的仅仅是一个32位的整型索引。当尝试用这个索引去访问ipc entry table对应位置的ipc entry时,就会出现问题。

攻击方式:利用这个漏洞,可以使高权限进程中的特权port的ipc_entry被释放,然后再利用我们拥有receive right的port重新占位(需要处理ipc_entry的generation number),使得原先发送到特权port的消息都会被发送到我们拥有receive right的port上,形成port消息的中间人攻击。

利用方法(macOS提权,Ian Beer提供的PoC的攻击流程):

(1)攻击目标是com.apple.CoreServices.coreservicesd服务(mach_portal的目标不同),launchd拥有这个服务的send right。

(2)攻击者通过漏洞使得launchd拥有的send right被释放。

(3)然后再利用launchd注册大量的服务,期望这些服务的port的ipc_entry会重用之前被释放的send right。

(4)这样,当任意进程通过bootstrap port尝试查找coreservicesd的服务端口时,launchd就会将攻击者拥有receive right的端口发送给它。

(5)攻击者的进程拥有coreservicesd的send right。可以通过中间人(MiTM)的方式,来监听任意进程与coreservicesd的通信。

(6)通过获取root进程的task port,来得到root shell。

2. CVE-2016-7644

漏洞说明:在调用set_dp_control_port时,缺乏锁机制导致的竞争条件,可能造成ipc_entry指向被释放的ipc_port,形成UAF。(GPZ Issue 965)

漏洞分析:

set_dp_control_port源码如下

\

在调用ipc_port_release_send释放port的send right的时候,没有加锁。两个线程通过竞争条件,可以释放掉一个port的两个reference,使得ipc_entry指向被释放的ipc port。

利用方法(mach_portal):

(1) set_dp_control_port的第一个参数是host_priv,需要通过root权限的task获取

(2) 攻击者分配一个拥有receive right的port,插入send right引用(ipc_entry)。

(3) 利用port descriptor消息将这个port发送给自己,使内核拥有一个这个port的send right引用。

(4) 调用set_dp_control_port将这个port设置为dynamic_pager_control_port,拥有一个send right。

(5) 利用mach_port_deallocate释放自己的send right。这时,这个port的包含两个send right计数:port descriptor和dynamic_pager_control_port。包含三个引用计数:ipc_entry,port descriptor和dynamic_pager_control_port。

(6) 利用两个线程触发set_dp_control_port的竞争条件漏洞,使得引用数减少2。这时,这个port的send right计数为0,引用计数为1。但是仍然有两个指针指向这个port:ipc_entry,port descriptor。

(7) 再销毁之前发送的port descriptor消息,释放最后一个引用,使ipc_port被释放。形成ipc_entry指向一个被释放的ipc_port,利用其它数据占位这个被释放的ipc_port,即形成UAF。 

3. CVE-2016-7661

漏洞说明:powerd对于DEAD_NAME通知的处理存在缺陷,导致攻击者指定的port在powerd进程中被释放,形成拒绝服务或port替换。(GPZ Issue 976)

漏洞分析:漏洞的详细分析参照Ian Beer的漏洞报告。这里简单说明一下漏洞的成因。

powerd进程创建pmServerMachPort用于接收相关的服务消息,同时在这个port上允许接收DEAD_NAME的通知。当接收到一条msgid为MACH_NOTIFY_DEAD_NAME时,就会从消息中的not_port字段取出port的值,然后调用mach_port_deallocate进行销毁。

\

之所以会造成漏洞,是因为这个DEAD_NAME通知的消息是简单消息(simple message)。简单消息的传递并不涉及底层ipc_port的引用计数的修改,这里的not_port仅仅是一个整型的数据,这就表示攻击者可以向mach_port_deallocate提供任意的port参数。如果这个port参数正好是powerd进程中合法的一个port,就会导致port的释放,例如当前进程的task port。一旦task port被异常释放掉,后续的一些以task port作为参数的函数调用极有可能失败。这时,若缺乏对失败函数的检查,就可能导致powerd进程崩溃。

攻击方法(mach_portal):

(1) powerd进程以root权限运行

(2) mach_portal的目的是导致powerd进程崩溃,再其重新启动后向launchd注册服务时,通过port中间人攻击,窃取其task port来获取host priv port。

(3) 具体的攻击方式如分析中所述,向powerd进程的服务端口发送DEAD_NAME通知消息,以0x103作为not_port的数值(在大多数情况下,0x103是mach_task_self的返回值),这就会导致powerd进程调用mach_port_deallocate释放掉自身的task port。

(4) 在调用io_ps_copy_powersources_info时,powerd进程就会通过vm_allocate,以task port作为参数尝试分配内存。由于task port已经被释放,这时vm_allocate分配内存失败。

(5) powerd缺少对于返回值的检测,就会访问一个非法的地址指针,导致powerd进程崩溃。

三. mach_portal源码文件

mach_portal包含了在10.*的设备上获取root shell的代码。下面简单说明一下源码中比较重要的各个文件的作用。

cdhash.c:计算MachO文件的CodeDirectory SHA1 Hash

disable_protections.c:将mach_portal进程提权至root,绕过沙盒的限制

drop_payload.c:处理iosbinpack中的可执行文件,socket监听端口,生成shell

jailbreak.c:越狱流程入口

kernel_memory_helpers.c:获取kernel task port后,内核读写的接口封装

kernel_sploit.c:set_dp_control_port竞争条件的利用,用于获取kernel task port

offset.c:包含设备以及系统相关的偏移量的初始化

patch_amfid.c:利用amfid的exception port来绕过代码签名

sandbox_escape.c:利用ipc_entry urefs和powerd的漏洞,获得host priv port,进一步攻击内核

unsandboxer.c:利用bootstrap port在父子进程之间的继承,监听子进程和launchd的通信,获取子进程的pid,通过提权,使mach_portal的子进程也绕过沙盒。

上一篇:Linux 内核 ipv4/udp.c 远程任意代码执行(CVE-2016-10229)
下一篇:服务器安全加固方案:安全策略修改密码
相关文章
图文推荐

关于我们 | 联系我们 | 广告服务 | 投资合作 | 版权申明 | 在线帮助 | 网站地图 | 作品发布 | Vip技术培训 | 举报中心

版权所有: 红黑联盟--致力于做实用的IT技术学习网站