macOS 10.12.2本地提权和XNU port堆风水

0x00 简介

从yalu 10.2 中,我们可以学到很多新的漏洞利用技术,特别是XNU堆风水和绕过内核保护。 在本文中,我们将讨论XNU堆风水的一些技术细节,并使用该技术在macOS中获得root权限。最后但同样重要的是,漏洞源码可以从以下网址下载:

https://github.com/zhengmin1989/macOS-10.12.2-Exp-via-mach_voucher

0x01 内核堆栈溢出

为了使用XNU堆风水在macOS 10.12中获得root权限,我们需要一个内核漏洞。在本文中,我们选择mach_voucher堆溢出为例,Mach_voucher_extract_attr_recipe_trap() 是可以在沙箱内被调用的Mach陷阱。这是在iOS 10和macOS 10.12中新添加的功能,但是它含有一个严重的缓冲区溢出漏洞。

1.png

在这个函数中,args->recipe_size是一个指向整数的用户态指针,所以mach_voucher_extract_attr_recipe_trap() 将通过copyin() 从用户态拷贝size的值。

2.png

然后该函数使用sz值在内核堆上分配一个内存块。但是,开发人员忘记了args-> recipe_size是一个用户态指针,然后将其用做copyin()中的 size 值。 我们知道用户态指针可能大于sz值,这将导致内核堆中的缓冲区溢出。

请注意,如果我们要在该地址上分配一个内存块,我们可能无法控制用户态指针的地址。不过这不是问题。如果遇到未映射的内存,copyin()函数会自动停止。 所以,我们可以在高地址上分配一个内存块,然后通过取消映射其余的内存块来控制溢出数据。

3.png

0x02 通过port进行堆风水

在iOS 10和macOS 10.12中,苹果添加了一个新的防护机制,用以检查在错误区域释放的攻击,因此我们无法使用经典的vm_map_copy(修改vm_map_size)技术来执行堆风水。另外,苹果在iOS 9.2和macOS 10.11中添加了一个空闲列表(freelist)随机化的机制,所以我们不能较容易地预测再分配的内存块的位置。 为了解决这些问题,我们需要一个新的堆风水技术。在Yalu 10.2中,qwertyoruiop使用OOL_PORTS来获取可用于执行任意内核内存读写的内核任务端口。 这种技术绕过了XNU堆中的所有防护机制。接下来,我们将讨论这种技术的细节。

Mach msg是XNU中最常用的IPC机制,大量消息通过 “complicated message” 发送。通过MACH_MSG_OOL_PORTS_DESCRIPTOR msg_type的”complicated message”,我们可以向内核发送out-of-line端口。例如,我们发送了32个MACH_PORT_DEAD ool端口(32 * 8字节=0x100字节)到内核的kalloc.256区域。

4.png

保存在mach msg中的ool端口都是ipc_object类型的指针,这类指针可以指向用户态地址。 因此,我们可以使用mach_voucher漏洞来溢出这些指针,并在用户态下修改一个ipc_object指针,使它指向一个伪造的ipc_object。 另外,我们也可以在用户态下为假的端口创建假的任务。

5.png

为了保证溢出正确的ipc_object指针,我们需要做一些堆风水操作。 首先,为了确保新分配的内存块是连续的,我们向内核发送大量的ool端口消息。 然后使用在中间收到的一些消息戳一些slots。 然后我们再次发送一些消息,使得溢出点在slots的中间部位。 最后,我们使用mach_voucher在溢出点触发堆溢出。

6.png

溢出之后,我们可以接收到剩余得 mach 消息,来找到被破坏的端口(不是MACH_PORT_DEAD 端口)。

7.png

0x03 内核内存任意读写

首先,我们将伪造的ipc_object的io_bits设置为IKOT_CLOCK。所以我们可以使用clock_sleep_trap() 通过遍历内核来获取内核中计时任务的地址。 这个地址将帮助我们稍后找到内核数据。

8.png

然后我们将伪造的ipc_object的io_bits设置为IKOT_TASK,为假的端口制造一个假的任务。 通过将值设置为faketask + 0x380(在arm64中为0x360),我们可以通过pid_for_task() 读取任意32位内核内存。这是因为该函数不检查任务的有效性,只返回((faketask + 0x380)+ 0x10)的值。 所以我们可以在没使用任何工具和ROP的情况下获得可造性内核读取。

9.png

10.png

通过计时任务泄露内核地址,我们可以在内存中搜索内核镜像的魔数,然后找到kslide。

11.png

获取内核基地址后,我们可以遍历所有进程来查找内核ipc_object和内核任务。然后我们可以dump出这些信息,给我们的伪造的ipc_object和任务。通过对假的ipc_object和任务使用task_get_special_port() ,我们可以获得内核任务端口。请注意,内核任务端口是非常强大的。它可以通过mach_vm_read()和mach_vm_write()进行任意内核内存读写。

12.png

0x04 root提权

每个进程的凭据信息,posix_cred结构,存储在内核内存中。首先我们需要找到我们的进程信息(通过内核基址+ allproc),然后找到我们的利用程序的posix_cred结构数据。 之后,我们通过kernel_task_port使用mach_vm_write() ,将cr_ruid(实际用户ID)值设置为0(也就是root)。最后,而且非常重要的是,我们可以使用system(“/ bin / bash”)获取root shell!

13.png

0x05 总结

在本文中,我们介绍了如何使用mach_voucher堆溢出和port堆风水来实现macOS 10.12.2上的本地提权。漏洞源码可以从下面的网址下载:

https://github.com/zhengmin1989/macOS-10.12.2-Exp-via-mach_voucher

0x06 参考

  1. Yalu 102: https://github.com/kpwn/yalu102

  2. https://bugs.chromium.org/p/project-zero/issues/detail?id=1004

原文作者:Vitaly Nikolenko译者:依然范特西 校对:布兜儿

原文链接:https://jaq.alibaba.com/community/art/show?articleid=781

转载地址:http://bbs.pediy.com/thread-217845.htm


发表评论

(必填)

(必填)

(以便回访)