英特尔正在推广一项很灵活的技术。该技术可以在处理器层面上阻挡恶意软件感染,其相关细节已经在上周四发表。实际上是这样:英特尔称之为控制流强制技术 (Control-flow Enforcement Technology, CET) 尝试阻挠会使用面向返回编程 (Return-orientated programming, ROP) 和面向跳转编程 (Jump-orientated programming, JOP) 的漏洞利用代码。
CET通过引入一个影子堆栈来工作。影子堆栈仅包括返回地址,它存储在系统RAM中,并受到CPU的内存管理模块保护。当子程序受到调用时,返回地址隐藏在线程的栈当中,通常而言,它也会在影子堆栈中。当处理器返回一个指令时,会确保在线程堆栈上的返回地址与影子堆栈中的地址匹配。
如果并不匹配,系统将抛出一个异常,让操作系统能够捕捉并停止执行。因此,如果漏洞利用代码开始篡改堆栈,将恶意指令串在一起,在系统上安装恶意软件或用其它方式攻击系统,这类篡改将会被检测到,并且可以在造成任何伤害之前制止它们。
影子堆栈必须在页表上具有新的影子堆栈集的内存上存储。如果软件尝试通过访问影子堆栈实现恶意企图,比如利用MOV指令,就会被内存管理单元和操作系统提出的一个页面错误警告所制止。如果软件在影子堆栈没有被标记为影子堆栈的情况下尝试使用控制流指令,比如RET,页表同样会提出一个错误警告。
运行中线程使用的影子堆栈指针 (Shadow stack pointer, SSP) 存储在任务状态段中。有各种各样的控制寄存器能够对特权ring 0到ring 2(非用户状态ring)和中断获取SSPs。如果想要了解这项技术的细节,可以参考这篇PDF文档。
你经常能做到这样的事情:找到某个软件中的内存缓冲区,并在数组中注入更多的数据,从而让多出的字节溢出到其它变量和指针的位置上。最终你将粉碎堆栈上的返回地址,并让它指向某个有效的 恶意代码 。当运行函数返回时,处理器不会跳回到软件的某个合法部分,而是跳回到你定义的重写堆栈部分,也即你的恶意载荷。
只要通过互联网进行传播,你就可以在其他人的系统上实现执行任意代码。他们的盒子现在变成了你的盒子。
然后,操作系统和处理器开始实施机制防止上述情况。那个堆栈存储在内存页表上的方式是数据,而不是可执行代码。因此,很容易在发生破坏之前解决这类攻击:如果处理器开始尝试执行在非可执行区域存储的代码,也即数据栈,系统将返回一个异常。这就是非可执行 (No execute, NX) 在页表中的作用。英特尔,AMD和ARM等厂商对该技术的称呼并不完全一样。
然后就是有意思的部分了:面向返回编程 (Return-orientated programming, ROP)。本质上来讲,你仍旧能够覆盖堆栈,用自己选择的值进行填充,但你的目的变成了创造一个全部指向运行中程序有用指令块的序列。对处理器而言,它仍旧在执行正常的代码,没有抛出异常。
这样理解它比较合适:这不是逐字逐句地按作者的原意读一本书,而是选择跳过其中的一些部分,这等于在原小说的基础上进行二次创作。这正是ROP的工作方式:你用小程序地址填充堆栈,每个小程序都以RET及其类似指令为结尾。当处理器跳到某个小程序时,它将执行其中的指令,然后轮到RET,从堆栈中取出下一个返回地址,跳到其指向的下一个小程序。
以下是两个小程序的例子:
pop %ebx
pop %eax
ret
mov %eax, (%ebx)
ret
第一个小程序从堆栈上取出两个值,并在变量ebx和eax中存储它们。别忘了,你控制着堆栈的内容,因此能够确保这些指令包含了你想要的数值。下一步,第二个小程序将eax的数值填写到ebx指向的内存地址。将这些小程序连起来,你就能编辑当前运行中线程所有能够改变的内存地址,这被称为任意写入,它在篡改应用和服务器方面非常有效。
最后,你将可以把足够多的小代码区域和参数缝在一起,要求操作系统将一个非可执行的内存区域标记为可执行,并保证它装满了你的恶意载荷,并跳转到它。因为你已经将其标记为可执行,处理器运行它时不会遇到什么问题,你也就完成了任意代码执行攻击。
事实上,ROP和JOP就是恶意软件作者用来获取受害计算机控制权的基本方法。
CET在这里的功能是,当从某个子程序返回时,堆栈还没有被恶意软件所劫持。没有ROP,漏洞利用就不会有效,也就不会有恶意软件感染。
无法通过通常的程序代码修改影子堆栈。当然,如果你能以某种方式欺骗内核,让它解锁影子堆栈,干涉它,让它和你的ROP链条一致,然后重新启用保护,就能够绕过CET。
英特尔网络安全专家Matthew Rosenquist说:“英特尔开发的控制流强制技术为未来利用中央处理器的固定硬件架构,建立控制,帮助防止并干预代码重用攻击提供了一个方向。”
“通过使用影子堆栈、指针和其它机制,CET建立的机制能够防止有人滥用合法代码。”
目前,CET处于预览阶段,还有许多工作尚待完成。该规范是在微软的协助之下完成的,已经开始在小范围内测试,获取技术反馈。在CET的设计之下,可能会有一些没有考虑到的部分可能被利用,回避保护机制。它是个非常复杂的系统,和许多芯片级的特性相关,比如特权级别、中断、管理程序入口和出口。在这项技术真的落实到产品上之前还有一段路要走。
前几年就已经有一些计算机科学家提出了影子堆栈的概念。在这项技术成为主流之前,抵抗ROP漏洞利用的主要方式是ASLR,也即地址空间随机化。这一操作系统级的特性会在程序的虚拟空间中随机为程序的组件分配地址,比如它的库和可执行文件。由于代码存储的空间在每次程序开始执行之前都会被随机化,小程序的位置也会改变,因此精心准备的ROP栈将不再有效:它指向的是错误的位置,会导致应用或服务器崩溃。
ASLR并不是万无一失。有时黑客能够通过信息泄露漏洞获取小程序的基地址,进而计算并确定其位置,实现跳转。