CVE-2018-4407 XNU内核漏洞详细分析

作者:maldiohead@ 360 NirvanTeam

漏洞简述:

CVE-2018-4407是XNU内核的BSD网络模块的一个内核堆溢出漏洞,可以造成内核崩溃

受影响的系统有:

  • macOS 10.13.6及以下
  • iOS11 及以下

 

漏洞分析

twitter上的poc:

send(IP(dst=“Target IP“,options=[IPOption(“A”*8)])/TCP(dport=2323,options=[(19, “1"*18),(19, “2”*18)]))

本次分析以这个poc为说明。

通过wireshark抓包,根据ip头数据结构,发现ip头数据是没问题的,然后根据options的结构体发现ip Options的数据格式不合法。Ip options主要是用来测试,调试等目的用的。

IP OptionsIP options 的结构说明

公开的poc网络请求的数据包填充了8字节的options数据。下图是抓包得来的数据。

Poc中的ip options的数据

可以发现ip options的数据完全不合法,

通过分析XNU内核代码,我们找到了处理ip options的相关处理函数ip_dooptions

文件位置(darwin-xnu\bsd\netinet\ip_input.c),在函数ip_dooptions中会循环解析options中的数据,如果中间某次出错就会直接goto到bad流程中。结合poc我们会看到这里optlen的值是0x41, cnt是0x8所以这里会从3256行直接跳到bad流程中,而在bad流程中则会调用icmp_error函数(icmp_error的目的是构造一个类型错误的数据包,来响应来自远程的错误的ip数据包)。

解析ip option数据

在函数icmp_error中在下面这句导致了溢出:

m_copydata(n, 0, icmplen, (caddr_t)&icp->icmp_ip);

这是一个内存拷贝函数,会拷贝大小为icmplen长度的对象n中数据到icp->icmp_ip这个缓冲区中。所以对于这个内存拷贝函数,需要确定icmplen和目的缓冲区大小,就明确了为什么会产生这个漏洞。首先我们看一下icmplen的长度,通过分析会发现icmplen由下面这句代码确定:

icmplen = min(oiphlen + icmpelen, min(nlen, oip->iplen));

这里oiphlen是ip头与ipoptions之和即28字节,nlen是原始packet的长度,这里必然是大于oip->ip_len的,而oip->ip_len的长度是88字节,由于这个包是TCP包。所以icmpelen由281行代码确定(如下图)。

icmplen相关代码

通过分析数据包及代码,可以知道tcphlen长度为60字节 ,icmp_datalen等于8,oip->ip_len等于88,所以icmpelen的值是60。分析完了所有与icmplen值相关的数据,最终确定icmplen这里的值是88。

所以接下来就是分析icp->icmp_ip缓冲区的大小,在294或296初始化一个mbuf,而icp就是属于mbuf对象的m_data成员。所以这里首先要判断m到底是由哪一行决定的。

分配m对象

根据前面的判断,我们可以知道sizeof(struct ip) + ICMP_MINLEN + icmplen)

大小是88,所以对于到底值调用294行的函数初始化mbuf还是296行,主要是看MHLEN的大小,查看了MHLEN的宏定义,由于m_hdr ,pkthdr的结构有点复杂

MHLEN定义

所以想直接知道MHLEN的准确值比较麻烦。于是我进行了内核调试,获取了这个值。

MHLEN得值

很明显MHLEN的值是0x57也就是87,所以mbuf的初始化就在296行这里进行的。

也就是调用m_getcl函数,对于这个函数,可以看到在3732行进行了mbuf对象内存的申请,mcache_alloc这个函数也是相当复杂的,这里对于分析这个漏洞没多大关系,就不再关注具体是怎么分配对象了。。

初始化mbuf

重点是MBUF_INIT这个宏代码(如下图),这里首先在888行判断pkthdr的值是否为0,结合上图的3726行代码,就会发现mbuf->m_data指针指向的是m_pktdat.

MBUF_INIT代码

对于m_ pktdat 定义:

#define     m_pktdat        M_dat.MH.MH_dat.MH_databuf

和mbuf的定义:

mbuf的定义

所以我们知道了m->m_data的大小是87字节。在这里icp指向的地址是m->m_data

对于代码:

m_copydata(n, 0, icmplen, (caddr_t)&icp->icmp_ip);

结合前面的计算,我们知道了icp->icmp_ip就等于 &m->m_data[7]

,而此刻icmplen的值是88,icp->icmp_ip的大小之后87-8=79 ,所以拷贝过程中就造成了堆内存溢出。

对于Twitter上的poc来说,只要构造出来的数据包大于79字节即可造成溢出,

同时对于ip options并不需要填充8字节,只要能够造成在调用到bad流程就可以,经过我的测试,这里ip options最小只需要1字节即可。总体的数据包大于79,就可以造成溢出。从而导致内核崩溃。

 

这个漏洞能利用吗?

通过分析,可以发现mbuf最大长度是2000字节,所以我们最大可以构造这么大的长度的数据包,来存放我们的shellcode,但是要想稳定利用,还得堆喷和内核信息泄露的漏洞结合起来,来达到远程代码执行的目的。

所以单个的这个漏洞很难实现稳定的远程代码执行的目的。

 

引用:

  1. CVE-2018-4407 poc:

https://twitter.com/ihackbanme/status/1057811965945376768

  1. Structure of the IP Datagram:

http://www.rhyshaden.com/ipdgram.htm

  1. Kernel RCE caused by buffer overflow in Apple’s ICMP packet-handling code

https://lgtm.com/blog/apple_xnu_icmp_error_CVE-2018-4407

  1. Memory Buffers, Socket Manipulation, and Socket Input/Output

https://developer.apple.com/library/archive/documentation/Darwin/Conceptual/NKEConceptual/SocketManipulation/SocketManipulation.html

(完)