微信公众号 mindshare思享
前面几篇文章已经讲过来Speculation和cache side channel attack. 有了这些知识,终于到了讲Google Project Zero (GPZ)的Spectre和Meltdown的时候了。
Spectre和Meltdown的共性就是都是利用了CPU的Speculation和cache side channel attack. 但也有不同点:
Meltdown是直接跨过了CPU硬件定义的特权级权限,让更低特权的软件有机会获取到高特权级的数据。
Spectre本身没有跨过CPU硬件定义的特权级权限,但是软件可以利用一些特定代码实现获取高特权或同等特权级的数据。
Spectre/Meltdown总览
到目前为止共有四个spectre/Meltdown的问题被揭露。
最近的是今年5月披露的Spectre variant4. 相信随着对各CPU厂商的微构架的更多发掘,也许还会有更多的问题被发现。
本文先讲Spectre Variant1.
Spectre Variant1: 跨过软件边界检查
原理概述
对应特权软件(比如OS)说,通常它会检查不可信任的软件(比如application)传进来用来作为访问(特权/信任软件的访问)一个数组或是结构体的offset. 很多常见的攻击方式都会通过访问指针越界来实施,比如Linux kernel很多User interface的检查,Java对数组的边界检查等。
软件一般可以这样写,
在现代的CPU里,为了高性能,就像在之前文章里所说的,它可以在边界检查if(untrusted_offset_from_user < arr->length还没有确认是否越界的情况下 specualatively去做value=arr->data[untrusted_offset_from_user]的访问。
当CPU在可信任的软件里运行以上代码时,不可信任软件可以传入任意untrusted_offset_from_user值,都有可能被bypass边界检查而使arr->data[untrusted_offset_from_user]被speculatively访问。
正如前文所述,如果真正在architecture执行的时候发现(untrusted_offset_from_user< arr->length条件不满足,“value =arr->data[untrusted_offset_from_user]”中的value不会真的设置为arr->data[untrusted_offset_from_user].但是” arr->data[untrusted_offset_from_user]”这个值会因为speculation进入到cache里面。
接下来我们需要利用这个speculative的值去检索一个untrusted buf: arr2.
比如我们想知道value (arr->data[untrusted_offset_from_user]bit0的值是0还是1的话,可以做如上操作。如果是0的话,arr2->data[0x200]被speculative访问,进入cache;如果是1的话,arr2->data[0x300]被speculative访问,进入cache.
Untrusted软件现在需要利用cache side channel attack的办法,访问arr2->data[0x200]和arr2->data[0x300]并测量访问时间,就知道arr->data[untrusted_offset_from_user]的bit[0]是0还是1.
在现代的CPU里,speculation甚至可以多次跨过边界检查,比如以下代码,speculation可以跨过第二次的边界检查,
因为untrusted_offset_from_user可以是任意地址,所以理论上可以将所有的敏感数据泄露。
为加强攻击效果,有的时候攻击软件可能会通过训练分支预测,然speculation更容易发生。可以这样做:
在传入攻击untrusted_offset_from_user之前,多次传入正常不会越界的untrusted_offset_from_user,训练分支预测硬件,以使其预测if (untrusted_offset_from_user < arr1->length)条件成立。
然后突施杀招,传入攻击用的非正常untrusted_offset_from_user,由于之前的训练,CPU硬件比较容易speculatively访问value =arr1->data[untrusted_offset_from_user];
基于这个原理,所有这种攻击可以发生在:
1.JIT引擎, Javascript,kernel空间的eBPF bytecode解释器。使JIT的一个应用可以获取到另一应用的信息,比如一个browser的密码插件的信息。
2.一个虚拟机VM获取到另一个虚拟机VM的信息。
3.VM获取到hypervisor的信息
4. Application可能获取到OS的信息
利用这个问题来攻击需要找到这样的代码序列,并且需要一个可以probe的buff,并且由于在guest OS切换到App, 或是Hypervisor切换到guest OS的过程中有很多的软件save/restore的过程,在现实世界里,这个攻击是比较难实施的。
比较现实的攻击是对JIT,JavaScript code, 比如JavaScript code运行在一个沙盒里,JavaScript引擎可以解释执行或是JIT编译执行,但是Java引擎或是JIT编译器会保证:
1.在这个沙盒里不允许访问不能访问的内存
2.没有指针,数组访问都会做边界检查
但是由于这个Spectre问题,会导致边界检查时speculative bypass, 出现沙盒的信息泄露。
软件Mitigation(缓解办法)
由上所述,Spectrevaraint1的是speculation导致的,但是现代的CPU Speculation的特性是Natural的(有生具有的),就是说Speculation是没法简单去掉的。如在上文通俗篇所述,简单去掉speculation会在比较大的程度上影响到性能,而且绝大多数的时候,Speculation有益无害。
那么只能想一些缓解的办法。
题外话,英文里的Mitigation是指缓解,不是完全修复。
这个办法就是定义一个新类型的barrier指令,这个指令的作用就是告诉PCU硬件在这个地方不要做speculation.软件工程师在认为可能出现Spectre variant1安全问题的地方插入(当然需要软件工程师手动加,不要指望Compiler帮忙)这个新的barrier指令。
arm新加一条CSDB 有条件speculation barrier指令,这个指令的作用是,
在这个barrier完成之前
1)在CSDB程序顺序之后,和条件选择指令/或非直接跳转指令有地址依赖关系的,任何读写和预取指令,
2)这些指令在条件选择指令/或非直接跳转指令条件明确之前,不会影响到cache allocation。
举个例子,同样对于下面的代码,
它可以生成如下指令
我们可以在if (untrusted_offset < limit)之后插入新的barrier指令,
Intel的情况类似,Intel推荐使用LFENCE 指令,它阻止老的指令retire之前新的指令被speculative执行。
这个问题mitigation的困难之处在于,需要让无辜的软件工程师编写代码时思考是否为spectrevariant1的情况加barrier。还记得理解和使用mb, smp_mb, wmb等其他barrier的痛苦吗?
对于arm来说,可用的mitigation已经包括,
· 在compiler toolchain里加入对新的barrier的支持。
· 在Linux kernel的eBPF中
· https://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux.git/tag/?h=security_fixes_20180118
Variant 1 – genericebpf fix from list arm32and arm64 support being reworked for new Dan Williams proposal
Spectre Variant1 结语
因为Spectre variant1是性能和安全的鱼和熊掌不能兼得的问题,虽然各CPU厂商提出来缓解的方法,但是挑战和困难之处在于需要软件工程师找到合适的地方实施这些缓解方法。
待续
相关阅读
Spectre/Meltdown演义-专业篇(3)