处理器微架构数据采样攻击概述

今天, 我选择的几篇论文读的差不多了, 课程的报告也写完了, 现同步发布在这里。老师说只需要1~2天的工作量, 然而我做了一周多, 我太菜了。另外, 我对处理器体系结构不是很精通, 可能文章中有若干错误, 欢迎在留言区与我讨论哦。

感谢 福报(foobar)院 计算机系统结构学部专家 MonKey 指点。

在2018年前后,Meltdown和Spectre这两个针对处理器微架构的侧信道攻击方法被提出。此后,安全研究人员对处理器,特别是Intel x86处理器,微架构的研究越来越深入,也逐步发现了各种各样的设计缺陷以及对应的侧信道攻击方法。Intel将这些大部分设计缺陷的攻击方法统称为微架构数据采样(MDS,Microarchitectural Data Sampling),同时,研究人员为这些攻击建立了MDS Attacks网站[7]。本文所讨论的大部分问题也选取自该网站。

具体而言,为了优化性能,处理器的微架构中引入了大量的组件和逻辑电路,用于加速指令执行和内存访问,同时保持指令集架构(ISA)层面的规范不被违反。这些组件包括缓存(Cache)和TLB(俗称“快表”)、Line Fill Buffer、Load Ports以及Store Buffer等,它们都具有临时存储数据的能力而成为攻击目标。后文所讨论的攻击方法的原理与Meltdown和Spectre类似,攻击者首先通过瞬态执行的读指令从上述某些组件中读取一些数据,然后将读取到的数据编码后存储在外部可观测的微架构状态中。攻击者随即通过一些方法测量这一微架构状态,从而解码获得最初读到的数据,完成微架构数据采样。这一过程中,这些瞬态执行的指令最终可能因分支预测错误或产生异常而被放弃,也可能成功执行而被提交。而无论瞬态执行的指令被放弃还是被提交,这些外部可观测的微架构状态都应当不被处理器复原,以便攻击者后续进行测量。常用的状态为缓存的状态,同时也有攻击方法使用TLB的状态。这些状态也被称为隐蔽信道,即缓存隐蔽信道或TLB隐蔽信道等。由于处理器,特别是Intel处理器的设计和实现时采用了十分激进的优化手段(本文猜测这些优化手段是为了优化处理器电路的面积和延迟),瞬态执行的指令在读取上述组件中的数据时,处理器会跳过很多关键的安全检查,包括内存页权限检查以及特权级检查等。因此,上述方法从组件中读取的数据很可能是敏感的、机密的,如操作系统内核的数据或者SGX enclave中的数据。这意味着普通进程可以跨越安全边界(如隔离边界或特权边界)去获取程序正常执行情况下本无法获取的数据,造成数据安全风险。

后文将更具体地讨论上述组件原本的功能、对应的攻击方法以及扩展的攻击方法。后文也称能够缓冲数据的上述组件为处理器内的缓冲区。

Line Fill Buffer

现代处理器的缓存很大,但不是无限大。因此,在程序运行过程中,总会出现缓存缺失的情形。由于在内存之上可能存在不止一级缓存,此处考虑L1缓存。当某一条内存访问指令目标数据的缓存缺失时,对于读操作或者采用了写分配缓存策略的写操作,处理器会将目标数据所在的缓存行从L2缓存中取出并存储到L1缓存中,这一过程需要一段时间。此时,该指令之后的不依赖该指令结果的指令,特别是之后的内存访问指令,事实上仍然有机会开始执行,之后的内存访问指令也仍然有可能命中缓存。为此,处理器这时不会等待这一过程完成再执行接下来的内存访问,而是在Line Fill Buffer中创建一个新条目,将必要的信息(包括物理地址等信息)记录在其中,并直接继续之后的执行。当需要的缓存行从L2缓存中取出后,其会被存放入Line Fill Buffer的上述条目中。然后,处理器会将该缓存行存储到L1缓存中,这一过程也需要一段时间。这说明,数据会在Line Fill Buffer中停留一段时间。一旦数据出现在Line Fill Buffer中,等待该数据的内存访问操作就可以继续执行。此外,若其他内存访问操作的L1缓存缺失了,但所需的数据在Line Fill Buffer中,那么该操作就可以直接使用这个数据。

上文讨论的是Line Fill Buffer用来实现非阻塞缓存的功能,Line Fill Buffer的其他功能还包括:

  • load压缩:若后续的一个或多个对相同物理地址的内存访问操作都造成了缓存缺失,同时Line Fill Buffer中已经有条目在等待该物理地址的数据,那么这些内存访问操作可以直接对应到这一个条目。
  • 非时间请求:某些内存访问是已知没有时间局部性的,对于这些内存访问,数据仅暂存在Line Fill Buffer中,而不进入缓存。

安全研究人员发现,某些内存读指令在瞬态执行的过程中会读取到Line Fill Buffer中某个不确定条目中的数据,这些读指令一般是最终会产生异常或需要微码辅助执行的指令。例如,一条读取某个新页面(新分配的,但还没有映射到物理页面)的内存读指令执行时会产生一个良性的缺页异常,在瞬态执行的过程中,它就会读取到Line Fill Buffer中某个不确定条目中的数据。接着,攻击者就可以通过上文所述的通用方法来获得(泄漏)该数据了。这一攻击方法被称为RIDL攻击[1]。现代处理器为了提高物理核心内部组件的利用率、提升总体性能,在每个物理核心中设置了两个硬件线程。这两个硬件线程有一些独立的资源,剩下的组件是共享的,Line Fill Buffer就属于共享的组件。于是,一个硬件线程还可以通过RIDL攻击泄漏另一个硬件线程的数据。

RIDL与此前的攻击的不同之处在于,后者,如Meltdown和Spectre,大都需要已知被泄露数据(即受害者)的地址来进行攻击,而RIDL则不用,RIDL能够无需内存地址、“随机”地从Line Fill Buffer泄漏敏感数据。

此外,这一攻击的目标还可以是Load Ports,原理也是类似的。

Store Buffer

现代处理器的缓存很快,但不是无限快。内存写操作需要将数据写入缓存内,但缓存的速度仍然不及处理器流水线。为了使得处理器不被内存写操作阻塞,处理器内部设置了Store Buffer,内存写操作将目标地址以及数据放入该Store Buffer中的一个空位后,处理器即可继续乱序执行。当然,若Store Buffer已满,则仍然需要阻塞,直到Store Buffer出现空位。与此同时,Store Buffer慢慢地异步地将这些写操作写入缓存内。为了维护程序序(program order),即让在内存写操作之后的(此处为程序序)对相同地址的内存读操作能够读取到最近写入的数据,同时为了不阻塞该读操作到写操作完成,处理器引入了store-to-load转发。为了实现store-to-load转发,读操作在访问L1缓存时,同时还在Store Buffer中查找最近的相同地址的写操作,若有匹配(完全匹配,或读取和写入的地址区间有所重叠),则直接转发相应写操作的数据给该读操作进行后续处理。这一地址匹配分为两个阶段,低地址匹配(一般仅匹配低12位,它们是页内偏移,不需要经过页表或TLB转换)以及完整物理地址匹配。

安全研究人员通过分析Intel的专利注意到,在瞬态执行过程中,低地址匹配成功后,若读操作的物理地址还未准备好(即TLB缺失),处理器会假定完整物理地址匹配是成功的,并将数据转发给该读操作(如果之后发现预测错误,该读操作会被取消或者重新执行)。接着,攻击者就可以通过上文所述的通用方法来获得(泄漏)该数据了。这一攻击方法被称为Fallout攻击[2]。

Data Bounce

Fallout的作者进一步注意到,若在处理(攻击者已知是相同地址的)读操作时,最近的内存写操作的目标物理地址还未准备好(即TLB缺失),那么store-to-load转发会失败,这包括虚拟地址有效但TLB缺失的情况,也包括虚拟地址无效的情况。攻击者可以利用这一发现来探测某一虚拟地址是否有效,即是否映射到了物理页面,从而助力破解内核地址空间布局随机化(KASLR,一次又一次地被破解)。其首先在瞬态执行环境下写某个虚拟地址,然后读同样的虚拟地址,最后将读出的内容通过缓存侧信道传递到正常执行环境,接着进行判断。若store-to-load转发成功了,则说明该虚拟地址映射到了物理页面,否则没有映射。

为了进一步将TLB缺失和虚拟地址无效区分开来,作者提出了Fetch+Bounce方法。该方法重复进行多次Data Bounce,直到store-to-load转发成功或者重试次数超过限制。若store-to-load转发一次成功,说明虚拟地址有效,并且TLB命中;若store-to-load转发第二次才成功,说明虚拟地址有效,但第一次TLB缺失了;否则,说明该虚拟地址是无效的。

ZombieLoad

ZombieLoad [3]是指某些执行过程中出现问题而需要微码辅助重新执行的load操作,这些load操作也可能泄漏数据,ZombieLoad攻击就利用了这些load操作。目前,根据公开的资料,造成ZombieLoad攻击的根本原因还不完全明确,似乎与Line Fill Buffer也有关联,同时也与微架构中其他组件有关。与RIDL不同,ZombieLoad攻击可以在一些不受RIDL或Fallout影响(修复了RIDL或Fallout缺陷)的处理器上成功实现。

ZombieLoad的作者总结了几种攻击之间的区别与联系:

  • Meltdown攻击允许攻击者获得任意给定虚拟地址(48位)内的数据;
  • Fallout攻击允许攻击者在给定页内偏移(12位)的前提下,获得某一不确定页面内的数据;
  • RIDL以及ZombieLoad攻击则允许攻击者获得某一不确定缓存行中的数据,缓存行内的偏移(6位)可以选择。

接下来,本文讨论几种扩展的攻击方法。

CrossTalk

上述这些攻击方法都是应用在同一个物理核心的一个或两个硬件线程上的。为了研究跨越物理核心的情况,安全研究人员设计并实现了CrossTalk分析框架[5]。该分析框架分为两个阶段,第一个阶段利用性能计数器分析x86指令集中所有指令的涉及物理核心外的读写操作。

分析结果确认了某些指令涉及物理核心外的读写操作,这些读写操作应当是借助处理器内部多个物理核心之间的互联总线来完成的,这些指令包括cpuid(获取处理器的属性信息)、rdrand(获得一个随机数)、rdseed(获得一个随机种子)以及rdmsr(读取MSR寄存器)等。结合框架的分析结果,以及利用上文提到的微架构数据采样的方法,CrossTalk的作者发现这些指令会将该指令本身的结果放入某个全局所有物理核心共享的缓冲区的某一段区域内,然后将该共享缓冲区中的所有数据一起复制并存储在Line Fill Buffer中。作者称这个缓冲区为staging buffer,同时,Intel确认了有这样功能的缓冲区的存在。需要注意的是,此时,其他物理核心之前的指令残留在该共享缓冲区内的数据也会被一并复制到当前物理核心的Line Fill Buffer中。之后,攻击者利用上文提到的微架构数据采样攻击即可将其泄漏出来。

分析框架的第二个阶段利用上述分析结果,分析指令对上述共享缓冲区的修改情况,包括写入的值的类型(常量或未知的值)以及写入的位置(偏移量),为实际开展攻击做准备。

在上面的分析结果支撑下,攻击者可以进行CrossTalk攻击来跨越物理核心泄漏数据了。攻击流程大致如下:

  1. 攻击者核准备好攻击环境,如刷新(flush)一遍用于Flush+Reload的缓冲区数组。
  2. 攻击者核开始不断地执行cpuid指令。这会导致共享缓冲区中的所有数据被复制到攻击者核的Line Fill Buffer中。
  3. 同时,攻击者核不断地利用RIDL等攻击方法来泄漏Line Fill Buffer中的数据,并对数据进行处理。
  4. 使用减速攻击,迫使受害者核执行需要被泄漏结果的指令,如rdrand

作为一个攻击实例,作者实践了用此方法泄漏SGX中rdrand的结果,然后还原SGX中ECDSA算法的签名密钥的过程(该算法签名时需要一个安全的随机数,而SGX中的“可信”随机数来源只有rdrand)。

CacheOut

上文提到的攻击的缓解措施之一是在关键的时间点(如切换安全域时)刷新处理器内部的各种缓冲区,Intel也实现了该缓解措施,并修改了verw指令在微架构层面的行为,使其能够主动地清空这些缓冲区中的数据。这样,同一核心之后的指令虽然仍然可以泄漏这些缓冲区中的数据,但泄漏出的数据是没有意义的了。然而,对于攻击者而言,“有条件要上,没有条件创造条件也要上”。于是,攻击者可以通过某些方法主动地、间接地令需要泄漏的数据出现在这些缓冲区中,然后再进行同样的微架构数据采样攻击流程。CacheOut攻击[6]通过将缓存中的数据驱逐出缓存来实现这一点。CacheOut攻击方法对于泄漏受害者写过的数据和泄漏受害者读过的数据有不同的流程。

对于受害者进行过的内存写操作,该写操作会使得被写入的区域对应的缓存行被设置为脏的(数据被修改过,但还未写回下一级缓存或内存)。之后,攻击者立即通过访问合适数量的、地址经过精心选择的内存单元来产生缓存冲突,并将受害者的缓存行驱逐出缓存。在驱逐时,若缓存行是脏的,则缓存会对该缓存行进行写回。CacheOut的作者猜测并验证了,在缓存行被写回时,处理器首先将该缓存行的数据转移到Line Fill Buffer中,然后再慢慢地异步地写回至下一级缓存或内存。此前的研究虽然观测到了这一点,但是没有对其进行详细分析。数据就这样出现在了Line Fill Buffer中,使得攻击者可以利用上文所述的方法来泄漏了。甚至,在将缓存行驱逐出缓存之前,还可以利用verw指令来主动地清空处理器各种缓冲区(包括Line Fill Buffer)中的数据,降低后续泄漏过程中的噪声,辅助攻击。然而,极为讽刺的是,verw指令原本恰恰是用来防御这些的攻击的!这种对于受害者写操作的攻击不要求受害者和攻击者同时执行,先后执行即可。

对于受害者进行过的内存读操作,由于读操作不会将干净的缓存行设置为脏的,攻击流程有所不同。在这种情况下,攻击者需要和受害者在同一个物理核心的两个硬件线程上同时执行。攻击者线程不断地将目标缓存行驱逐出缓存,同时使用上文所述的方法来持续泄漏Line Fill Buffer中的数据。此时,受害者线程的内存读操作由于目标缓存行被驱逐出缓存了,出现了缓存缺失。然后,如上文Line Fill Buffer一节所述,处理器在处理缓存缺失时会先将数据从下一级缓存中读取到Line Fill Buffer中,然后再写入缓存。于是,攻击者线程就能够泄漏得到受害者的数据了。

此外,上文提到,RIDL以及ZombieLoad攻击允许攻击者获得某一不确定缓存行中的数据。因此,为了泄漏有用的数据,攻击者需要设计数据过滤或噪声消除等方法。CacheOut攻击则能通过简单地选择被驱逐出缓存的缓存行来控制需要泄漏的缓存行。同时,CacheOut论文还提出了泄漏Line Fill Buffer特定偏移处的数据的新方法,进一步加强了该攻击的能力。最终,该攻击可以实现从缓存中转储某个页面的全部4KiB数据,包括SGX enclave内的数据。

LVI: Load Value Injection

事实上,上述这些攻击方法不仅可以泄漏受害者的敏感数据,还可以向受害者的瞬态执行流中注入数据,LVI就是指这样的攻击[4]。这个场景下,攻击者和受害者的角色互换了,攻击者此时是数据的产生(泄漏)者,其通过本节后文所述方法故意在处理器中的缓冲区中留下一些恶意数据,并期望(强迫)受害者在瞬态执行的过程中使用这些数据;而受害者是数据的接收方,其执行的内存访问指令可能产生异常、遭遇TLB缺失、需要微码辅助或有其他特殊行为,从而无意地或被迫地使用了攻击者残留在缓冲区中的恶意数据,并用这些数据进行之后的瞬态执行。此时,攻击者注入的恶意数据会发挥作用,其操控受害者瞬态执行的数据流或者控制流,并最终将敏感数据通过缓存等隐蔽信道传递(泄漏)给攻击者。其中,攻击者可以在自己的线程进行操作并在上述缓冲区中留下恶意数据,也可以通过受害者对外暴露的接口,借助受害者已有的代码片段(gadget)来实现这一点。

若攻击者通过受害者已有的代码片段来放置恶意数据,此时,这一攻击方法自始至终是操控受害者自己的正常或瞬态执行流去放置恶意数据、读取并泄漏敏感数据,那些在切换安全域(如切换进程、切换特权级等)时进行缓存刷新的缓解措施都不起作用了。

此外,更恐怖的是,LVI作者分析认为,即使是部署了最先进的缓解措施的Intel处理器,也就是将瞬态执行过程中读取到的非法数据设为零的处理器,也会受到LVI攻击的威胁。

结束语

上述几种攻击中,数据泄漏的路径为:残留在缓冲区中的敏感数据——瞬态执行读取缓冲区中的数据——通过缓存隐蔽信道传递敏感数据。通过阻断路径中的一个或多个节点,可以防护这些攻击。

(1)对于残留在缓冲区中的敏感数据,缓解措施包括在切换安全域时刷新处理器内的各种缓冲区来清除它们。Intel似乎偏好实现这类缓解措施。(2)另外,也可以设计微架构内部的隔离机制,如利用已有的PCID(Process-Context Identifier,进程上下文标识符)来区分不同进程的虚拟地址空间,从根本上避免瞬态执行的指令跨越安全边界读取残留在缓冲区中的敏感数据。(3)最后,亦有论文在研究阻断缓存隐蔽信道、防止瞬态执行读取到的数据向外输送的方法。

顺便一提,这些攻击让用户更清楚地认识到,SGX的“安全保证”仍然是指令集架构层面的。在微架构中,SGX enclave与普通执行流似乎并没有太多区分或隔离机制,从而一次又一次地产生安全问题。这也说明了微架构内部的隔离是十分有必要的。

随着安全研究人员对处理器微架构研究、逆向工程地更加深入、更加充分,在可以预见的将来,越来越多的处理器设计问题会被发现,被利用,然后被修复(然后进一步发现问题)。

参考文献

[1] RIDL: Rogue In-Flight Data Load (S&P ’19)
[2] Fallout: Leaking Data on Meltdown-resistant CPUs (CCS ’19)
[3] ZombieLoad: Cross-Privilege-Boundary Data Sampling (CCS ’19)
[4] LVI: Hijacking Transient Execution through Microarchitectural Load Value Injection (S&P ’20)
[5] CROSSTALK: Speculative Data Leaks Across Cores Are Real (S&P ’21)
[6] CacheOut: Leaking Data on Intel CPUs via Cache Evictions (??? ’20)

其他参考资料

[7] MDS Attacks: Microarchitectural Data Sampling
[8] Intel® 64 and IA-32 Architectures Optimization Reference Manual
[9] Last-Level Cache Side-Channel Attacks are Practical (S&P ’15)
[10] Prefetch Side-Channel Attacks: Bypassing SMAP and Kernel ASLR (CCS ’16)
[11] Telling Your Secrets without Page Faults: Stealthy Page Table-Based Attacks on Enclaved Execution (USENIX Security ’17)
[12] ASLR on the Line: Practical Cache Attacks on the MMU (NDSS ’17)
[13] RevAnC: A Framework for Reverse Engineering Hardware Page Table Caches (EuroSec ’17)
[14] Meltdown: Reading Kernel Memory from User Space (USENIX Security ’18)
[15] FORESHADOW: Extracting the Keys to the Intel SGX Kingdom with Transient Out-of-Order Execution (USENIX Security ’18)
[16] Translation Leak-aside Buffer: Defeating Cache Side-channel Protections with TLB Attacks (USENIX Security ’18)
[17] Malicious Management Unit: Why Stopping Cache Attacks in Software is Harder Than You Think (USENIX Security ’18)
[18] Spectre Attacks: Exploiting Speculative Execution (S&P ’19)
[19] SGAxe: How SGX Fails in Practice (??? ’20)

发表评论?

2 条评论。

  1. twd2 qq!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

  2. foobar院 院长、网络安全学部院士 twd2 同志 的综述令人受益匪浅。

回复给 Jack ¬
取消回复

注意 - 你可以用以下 HTML tags and attributes:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

:wink: :twisted: :roll: :oops: :mrgreen: :lol: :idea: :evil: :cry: :arrow: :?: :-| :-x :-o :-P :-D :-? :) :( :!: 8-O 8)

本文链接:https://twd2.me/archives/14536QrCode