聊一个与socket有关的内核漏洞

本来我是不想发的,应为这个漏洞的具体实现有很多地方我自己都搞不懂。但是后来想想好像发了也不能怎样啊你有本事你笑我狂笑别停笑的大声点!(对不起又中二了)然后就是希望能有大佬指正这里面可能存在的错误也顺便吸取一点经验。海涵撒

以下,拷贝自俺的内网博客。

浅谈一个iOS内核执行的利用原理

这个漏洞来自盘古的ISC2019的演讲。我在这里写下他作为今后的注记。希望能有一天回过头来,看看这篇文章,回味一下那位少年第一次明白一个漏洞时的欣喜还有一种说不出的激动。

首先我们来聊一聊socket的生命。Socket是一种很有意思的通信介质,他可以绑定在对外的端口上,可以绑定在文件上,也可以是内存中的一个指针。两个socket能相互连接,相互作用,传输数据,相亲相爱的过完简简单单的一辈子。两个正常的socket,会有两把雨伞,凭借着已经确认的眼神,漫步走在阿里园区,彼此交换心声,这是他们与生俱来的本领。

55

上面的代码需要一些头文件,类型和函数主要来自types.h/socket.h/un.h。这里我们创建两个socket来玩玩。接下来,我们要把socket绑定到文件上。

这里我们创建了sockaddr_un这个来自sys/un的结构体。我们清除他,并把他初始化。这里我们把sock的文件定在了桌面上。这么一来,我们就可以在bind函数中,把socket绑定到文件上。按照socket的惯例,当你绑定了一个socket到一个指定文件文件上的时候,这个文件的vnode就会被锁上。你没办方继续绑定其他的socket到这个文件上。

但是这里就有一个问题,创建文件并返回vnode需要大量的时间。这里大量的时间指的是储存设备跟不上处理的速度。于是,XNU就很“聪明”的临时解锁了这个socket。当请求的vnode被返回时,XNU会继续上锁并进行接下来的操作。我在XNU的源代码内找到了这样一条有意思的代码。(我其实自己也不确定是不是这里但是好像这个也能这个么玩

这里,当socket提交到bind的时候,bind函数会执行一些简单的检查比如socket是不是已经被shutdown了,是不是已经bind过了之类的。在检查完毕以后,在提交vnode请求之前,socket会被unlock。这么一来,我们依然有机会去race两个socket,去绑到同一个文件上面去。

于是乎,我们就可以循环尝试去bind这两个socket。(不知道为啥我两个小时持续bind一直没成功过)据盘古的大大说这个bind成功率很高,而且也没有打对应的patch。所以我很疑惑咯。

接下来,如果我们bind成功了,我们就能尝试去free一个socket。当你close这个socket的时候,会有一个结构体被释放。这个结构体被释放以后,我们就会拥有一个悬垂指针。(Dangling Pointer?)(Dancing Point!)接下来我们的目标就是把我们需要的数据写会这个结构体对应的内存里面。怎么做呢?

首先,我们在bind我们需要的socket之前申请4k个socket,并在我们需要的socket之后申请4k个socket。这样一来,我们在内存中就拥有了一大段连续的地址。接下来我们用double bind+free的方式创造一个悬垂指针。接下来,我们需要释放全部的socket并触发一次内核的Garbage Collect以便重新申请需要的地址。

这里我们申请去分配一大堆没用的mach port。如果我们在发送kalloc的时候,内核的响应时间比较长,那么应该就是出发了内核内存回收。在触发了内存回收以后,我们重新申请非常大的一段内核地址,这样一来就有很大的概率hit到之前的悬垂指针所指向的内存。

boom

接下来我们计算出悬垂指针所在的位置,按照offset写入我们需要的数据。接下来我们就可以利用socket.connect这个方法去掉用其中的一个被我们修改过的内核函数指针所在的函数。我们在调用之前,别忘了往寄存器内写入参数。这样一来,内核的任意执行就完成了。当然A12不允许你这么做我也没办法咯~

2019年秋

3 Likes

想到一个词,反复横跳可还行 :rofl:

1 Like

我就没成功过你咋成功的啊代码贴上来?

这个代码在哪