性能VS安全?CPU芯片漏洞攻击实战(1) – 破解macOS KASLR篇

作者:蒸米&白小龙@阿里移动安全

0x00 影响

早上突然就被Meltdown和Spectre这两个芯片漏洞刷屏了,但基本上都是一些新闻报道,对漏洞的分析和利用的信息基本为0。作为安全研究者,不能只浮在表面,还是要深入了解一下漏洞才行,于是开始研究这方面的资料。结果发现其实这个硬件漏洞的影响非常广,不光是Intel, ARM和AMD也受影响,只是AMD的影响比较小罢了。因此基本上所有的操作系统(Windows,macOS,Linux,Android等)都有被攻击的风险。漏洞有两种攻击模式:一种被称为Meltdown,是在用户态攻击内核态,造成内核信息泄露。另一种被称为Spectre,一个应用可以突破自己的沙盒限制,获取其他应用的信息。另外,因为是硬件漏洞,这个攻击对云的影响非常大,利用这个漏洞,一个guest可以获取host或同一台服务器上其他guest的信息,可以说是一个非常严重的漏洞,因此亚马逊和google都在紧急加班修复漏洞。比如google就公布了漏洞修复的进度在:https://support.google.com/faqs/answer/7622138。虽然是硬件漏洞,但是在系统或软件层面上通过牺牲性能的方法还是可以进行修补的。

0x01 原因

那么我们现在知道漏洞很严重了,那么漏洞形成的原因是什么呢?关键点在于Speculative execution(推测执行)。推测性执行是一种优化技术,CPU会执行一些可能在将来会执行任务。当分支指令发出之后,传统处理器在未收到正确的反馈信息之前,是不会做任何工作的,而具有预测执行能力的新型处理器,可以估计即将执行的指令,采用预先计算的方法来加快整个处理过程。如果任务最终没有被执行,CPU还可以回滚到之前的状态,就当做什么都没发生过一样。但是这样真的安全吗?答案是,并不安全。攻击者通过寻找或构建一些指令就可以在CPU回滚的时间窗口里进行一系列的攻击。比如Google Blog中提到的边界检查绕过(CVE-2017-5753),分支目标注入(CVE-2017-5715), 恶意数据缓存加载(CVE-2017-5754)

举个例子,如果CPU执行下面这段代码:

arr1->length没有被缓存的时候, CPU会从arr1->data[untrusted_offset_from_caller]处读取数据,如果untrusted_offset_from_caller的值超过arr1->length,就会造成越界读。当然,正常情况下这并不会出现什么问题,因为在执行到判断语句那一行的时候,CPU发现不对,后面的语句不应该被执行,于是会将状态回滚到越界读之前。

但是接下来,问题就出现了。假设arr1->length,arr2->data[0x200]和arr2->data[0x300]都没有被缓存,CPU会继续推测执行下面的代码,在这里index2会根据value&1产生两个不同的值0x200,0x300,而Value就是越界读到的值。接下来,代码会根据Value的值去读取arr2->data[0x200]arr2->data[0x300]的值并且这个值会被加入到缓存里。接下来,我们可以再次尝试去读取arr2->data[0x200]arr2->data[0x300],读取时间短的那个值说明被缓存过了,因此就可以判断出value&1的值为0还是1,从而做到内核信息泄露。

其实,在google的blog发布之前,就已经存在类似的攻击了,只是危害没有这么大而已,今天我们就直接实战一个利用Intel CPU芯片漏洞来破解macOS KASLR的攻击。

0x02 芯片漏洞实战之破解KASLR

这种攻击比较简单,但是是后面高级攻击的基础,因此我们先从这个攻击讲起。之前我们讲到,为了让CPU效率更高,它们依靠推测性执行在任务到来之前就提前执行任务。同样,数据预取就利用这个思想推测性地先将数据加载到缓存中。Intel的CPU有五个软件预取指令:prefetcht0,prefetcht1,prefetcht2,prefetchnta和prefetchw。这些指令作用是提示CPU,告诉他一个特定的内存位置可能很快被访问。然而,Intel的手册中却提到,预取“未映射到物理页面的地址”会导致不确定的性能损失。因此,我们可以通过CPU预读指令执行的时间长短来判断这个地址有没有被映射到物理页面上。

我们知道KASLR的原理是在内核的基址上增加一个slide,让攻击者无法猜测内核在内存中的位置。但是内核肯定是被映射到物理页面上的,因此我们可以使用预取指令去遍历内核可能的起始地址,如果执行预取指令的时间突然变短,就说明我们猜中了内核的起始地址。我们在网上成功找到了破解macOS 10.13 KASLR的POC,并做了一点简单的修改:https://pastebin.com/GSfJY72J//pastebin.com/GSfJY72J>

其中关键代码如下:

这是一段汇编,参数会传入想要预取的地址,然后利用rdtscp和rdtscp来统计指令执行的时间,并返回。于是我们从内核可能的起始地址开始,不断地执行这段汇编代码,直到我们找到内核的起始地址为止。

可以看到在0x15c00000这一行,指令执行的时间明显缩短了。因此,我们可以猜出Kernel Side为0x15c00000。

0x03 修复

根据某内部漏洞修复人员在twitter上的回复,苹果已经在macOS 10.13.2上对此类芯片漏洞进行了修复,采用了牺牲性能的针对用户态使用两次映射的方式来解决该问题。并号称10.13.3上有更好的解决方案。另外iOS的A*系列芯片暂时还不受这类漏洞的影响。

0x04 总结

本篇文章只是给芯片的一系列漏洞开了个头,我们随后还会有更多关于芯片漏洞的分析和利用实战,欢迎继续关注本系列的文章,谢谢。

参考文献:

  1. https://googleprojectzero.blogspot.hk/2018/01/reading-privileged-memory-with-side.html
  2. https://siguza.github.io/IOHIDeous/
  3. Prefetch Side-Channel Attacks: Bypassing SMAP and Kernel ASLR, CCS 2016.