0x00 概述
前几日爆出的包含Intel,AMD以及使用ARM架构的厂商的处理器中的重大漏洞可谓是在业内卷起轩然大波,这些漏洞可分为Meltdown和Spectre两类。本文就Google Zero Project,Meldown and Spectre以及博主所掌握的信息对目前漏洞机制,原理,应急预案进行归纳总结。
0x01 漏洞机制及影响对象
从目前所获得的信息来看,前者的影响更为显著,其实质是恶意数据缓存载入(Rogue data cache load (CVE-2017-5754)),打破了用户级页表与系统内核级页表之间的隔离,这种隔离机制的失效影响是非常大的,举个例子来说,你在银行的账号密码现在完全可以被也在用这个银行的恶意用户收集到。
后者,也就是Spectre,其实质是绕过式边界检测以及分支目标注入(bounds check bypass (CVE-2017-5753) && branch target injection (CVE-2017-5715)),影响波及范围控制在用户级页表之间,也就是破坏了应用与应用间的隔离,亦举个例子来说,现在你在微信上和家人朋友聊天的内容会被利用这些漏洞的人看到。
根据谷歌Project Zero的测试数据,对于Meldown漏洞,其影响的目标主要是Intel的CPU(Intel Haswell Xeon CPU等)。而Spectre漏洞,其影响的目标涉及Intel,AMD以及使用ARM架构的CPU(Intel Haswell Xeon CPU, the AMD FX CPU, the AMD PRO CPU 以及 an ARM Cortex A57等)。
0x02 漏洞原理
case 绕过式边界检测:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | struct array { unsigned long length; unsigned char data[]; }; struct array *arr1 = ...; /* small array */ struct array *arr2 = ...; /* array of size 0x400 */ /* >0x400 (OUT OF BOUNDS!) */ unsigned long untrusted_offset_from_caller = ...; if (untrusted_offset_from_caller < arr1->length) { unsigned char value = arr1->data[untrusted_offset_from_caller]; unsigned long index2 = ((value&1)*0x100)+0x200; if (index2 < arr2->length) { unsigned char value2 = arr2->data[index2]; } } |
在进一步分析上述代码前,先来讲一下分支预测和推测执行。
从Intel Optimization Reference Manual中得知,在Sandy Bridge及以后的CPU架构版本中,分支预测即在执行过程中对接下来的分支进行预测,并且提前执行。
自奔腾4之后,Intel就引入了Implicit Caching这一特性,将可能在接下来执行路径上用到的数据提前存储到缓存(L1 Cache)中,
而CPU可对L1 Cache中加载的数据以一种无序的方式进行推测,从而进一步在执行到该分支前处理好,也就是推测执行。
由此可见,分支预测和推测执行(下简称BP和SE)都是为了获得更好的性能,但这同时也埋下了隐患。
现在回到上文中的代码,假设现建立了两个数组,一个是目标小数组arr1,另一个是大数组arr2。
现定义一个大于arr1长度的untrusted_offset_from_caller,那么执行到if语句之前,CPU是不会对if语句之后的代码块进行BP/SE的,但倘若untrusted_offset_from_caller小于arr1的长度,那么执行到if语句之前,CPU推测到if语句之后的代码块需要进行BP/SE。由于这个特性,对于不同的untrusted_offset_from_caller,上述代码执必然会产生不同的结果,通过缓存侧信道攻击判断index2是0x300还是0x200即可知道arr1[untrusted_offset_from_caller]是0还是1,为了确保BP能够进行,在真正进行越界访问之前,需要不断使用小于arr1长度的untrusted_offset_from_caller来训练BP,使之认为if语句一般是true。
从上述解释来看,利用该漏洞有一个前提:攻击者的代码需要在存在于目标代码之中或注入到目标内存中执行,亦或利用解释器/JIT引擎来产生攻击代码。
case 分支目标注入:
从先前的研究得知在ASLR (Address Space Layout Randomization)存在使得两个被隔离的程序(Attacker && Victim)可以互相影响对方的BP的可能,而在最新的研究中,研究者发现这种影响可以扩大到Attacker重定向Victim代码的执行路径。
该漏洞的基本思路是Attacker通过ASLR中的漏洞,将包含Victim代码中间接性分支中已被加载进内存的目标地址的缓存行定向到主内存外,那么当CPU执行到该间接性分支时,在该缓存行被加载进CPU之前,CPU无法得知原来的数据定位在哪里,当然CPU也无法没有办法计算出数据的位置,而当该缓存行被加载在CPU之前,起码已经经过100次循环,这样就造成了CPU在SE该分支时会产生一个“空窗期”,利用好这段时间注入恶意代码,再结合SE的机制,即可实现攻击,具体的机制见Project Zero及Spectreattack上的Post与Paper。
case 恶意数据缓存载入:
Meltdown漏洞实质上也是借助于SE实现的,实际上,Anders Fogh的测试结果表明Intel CPU出于性能考虑对内存地址访问进行异步权限检查,对于内存地址读操作依然是立刻返回结果,如果权限检查未通过则在重排序缓冲区设置标识符,随后抛出异常,这就带来了一个问题,下面举个例子:
1 2 | mov rax, [Somekerneladdress] mov rbx, [someusermodeaddress] |
在userspace下第一个指令必然会导致异常,按常理来说第二个指令并不会执行,但事实上由于SE的存在,第二个指令在特定条件下是有可能执行的,倘若第二个指令执行,那么someusermodeaddress上的数据就回被载入缓存,那么在此基础上更进一步:
1 2 3 | Mov rax, [somekerneladdress] And rax, 1 Mov rbx,[rax+Someusermodeaddress] |
由前述可知,被加载到缓存中的缓存行会随着somekerneladdress中数据的不同而不同,这样一来,只要通过缓存侧信道攻击得知被加载到缓存中的缓存行,自然就可以得到somekerneladdress中的内容。为了保证后两个指令能够被SE执行,Anders Fogh将与上述代码不同的相关依赖的垃圾码(add rax, 0x141)置前,第一,保证了垃圾码只能一个一个按照顺序执行,第二,因为仅有两个单元可以执行上述指令,垃圾码占用了一个,那么另外一个单元只要没有被这些指令以外的指令占用的话,就可以保证上述指令能够被SE执行。
0x03 应急预案
Intel已经确认自身CPU中存在相关问题,并正与包括AMD、ARM和多家操作系统厂商在内的许多其他科技公司紧密合作,制定行业范围的方法,以便及时和建设性地解决这些漏洞,并且列出了受影响的CPU列表,相关页面如下:
https://security-center.intel.com/advisory.aspx?intelid=INTEL-SA-00088&languageid=en-fr
ARM确认大部分处理器不受漏洞影响,但给出了一个受影响的处理器列表。ARM认为,利用这些漏洞进行攻击需要在本地运行恶意软件,用户及时更新软件和不点击来历不明的链接会降低攻击风险。针对linux上的程序,ARM提供了新编译器,可用新编译器重新编译。另外发布了Linux ARM内核补丁,用于修补漏洞,相关页面如下:
https://developer.arm.com/support/security-update
AMD针对每个漏洞做了回复,第一个漏洞由软件、操作系统厂商发布补丁解决,性能影响非常轻微,其他两个漏洞由于AMD CPU特殊的架构,都不受影响。具体如下:
https://www.amd.com/en/corporate/speculative-execution
微软目前已经推出了补丁KB4056892 ,Linux在12月就已经推出补丁解决。
各大云服务厂商近日都会进行热升级修复该漏洞