浅析mach_portal(2)— CVE-2016-7644

上一篇博客我们简单了解了CVE-2016-7637,简单了解了mach ipc在通讯的过程中遇到的一些问题,这次我们来看有关mach ipc的第二个漏洞cve-2016-7644

基本概念

同步机制

前面几篇文章都介绍了Mach IPC的一些关于message的知识,实际上在Mach IPC中,不光有消息传递机制,还有一个同步机制,主要判定两个或者多个并发的操作如何访问共享的资源。

Mach的同步机制中有4个对象,最基本的就是互斥体,当别人使用资源时可以排除其他人对这个资源的访问。但是有一个特殊的要求:硬件对互斥体的进行原子操作,就是即使硬件中断也不打断互斥体的操作。

对象 所有者 可见性 等待
互斥体 1个 内核态 阻塞
信号量 多个 用户态 阻塞
自旋锁 1个 内核态 忙等
锁集 1个 用户态 阻塞

说到这里就要介绍一下锁组

大部分Mach同步对象并不是独立存在的,而是属于一个lck_grp_t(锁组) 对象。
在Mach和BSD中几乎每一个子系统在初始化时都会创建一个锁组

由上表可知互斥体和信号量都是阻塞等待的对象,阻塞等待就是如果锁的对象被其他线程所占有,那么请求访问的线程就会被加入到等待队列中,从而被阻塞。

接下来这个漏洞就是由于没有引入锁组机制形成条件竞争导致内核UAF

cve-2016-7644

这个漏洞是在调用set_dp_control_port函数时,由于没有锁组机制,造成了当两个线程同时调用时,会释放port的两个reference,调用两次ipc_port_release_send函数。
漏洞源码在vm_user.c中,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
kern_return_t
set_dp_control_port(
host_priv_t host_priv,
ipc_port_t control_port)
{
if (host_priv == HOST_PRIV_NULL)
return (KERN_INVALID_HOST);
if (IP_VALID(dynamic_pager_control_port))
ipc_port_release_send(dynamic_pager_control_port);
dynamic_pager_control_port = control_port;
return KERN_SUCCESS;
}

这个漏洞也比较简单,但由此也简单接触了另一个Mach IPC的机制同步机制,至于poc的分析我们将在最后一篇文章中一起介绍

Ian beer关于漏洞的write up这里

接下来我们来简单分析一下ian beer的利用代码(kernel_sploit.c文件中)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
......
mach_port_t send_ool_ports(mach_port_t to_send) {
kern_return_t err;
mach_port_t q;
mach_port_allocate(mach_task_self(),
MACH_PORT_RIGHT_RECEIVE,
&q);
......
void stash_port(mach_port_t p) {
ports_to_stash[n_stashed_ports++] = p;
}
......
int attempts = 0;
int max_attempts = 200000;
for (; attempts < max_attempts; attempts++) {
// set the dp_control_port
err = set_dp_control_port(host_priv, p);
if (err != KERN_SUCCESS) {
printf("failed: %s\n", mach_error_string(err));
printf("are you root? this is a root -> kernel bug\n");
exit(EXIT_FAILURE);
}
......

以上代码中,我们共拥有三组port的right和reference

  1. 通过mach_port_allocate函数拥有right和reference
  2. 通过stash_port,当stash_port没有free时,在ipc中存了一份right和reference
  3. 通过set_dp_control_port函数拥有right和reference
1
2
// drop our send right
mach_port_deallocate(mach_task_self(), p);

通过mach_port_deallocate,使得right为2,reference为3

当两个线程并发执行时,就会调用两次ipc_port_release_send函数,每执行一次这个函数,port的reference就减少一。这样release两次之后,right为0,reference为1。

“由于没有send right,所以会产生一个notify,这也是一个出发了这个条件竞争漏洞的标志”

文章目录
  1. 1. 基本概念
    1. 1.1. 同步机制
  2. 2. cve-2016-7644
|