SoK Practical Foundations for Software Spectre Defenses

SoK Practical Foundations for Software Spectre Defenses 论文分享

https://www.computer.org/csdl/proceedings-article/sp/2022/131600b517/1FlQz5KjQZ2

Sok: 软件幽灵防御的实践基础

这篇文章主要是以语义的形式研究幽灵分析的现有基础,讨论语义的不同选择,并描述每种选择的权衡

背景

幽灵漏洞

幽灵是最近发现的一系列漏洞,源于现代处理器上的推测执行,幽灵允许攻击者通过导致处理器错误预测控制流,比如说条件跳转或间接调用,或数据流的目标来学习敏感信息。

攻击者可用跨越安全边界窃取机密,比如说由进程抽象提供的硬件边界,由内存安全语言和基于软件的故障隔离技术提供的软件边界。

当处理器意识到自己预测错误时,它会回滚执行,抹去推测对程序员可见的影响,但是在推测执行期间,微架构状态,比如说数据缓存的状态,仍然会被修改,这些变化可能会在推测期间泄露,甚至在回滚之后仍然可以持续,因此攻击者可以从微架构状态中恢复敏感信息,即使这些敏感信息只是随机访问。

幽灵漏洞破解密码

High-assurance cryptography (高保证密码学)长期依赖于constant_x0002_time programming(常量时间编程),用来创建不受定时侧信道攻击的安全软件。

侧信道攻击是攻击者利用计算机不经意间释放出的信息信号,比如说电磁辐射,电脑硬件运行产生的声音,来进行破译的攻击模式,例如,黑客可以通过计算机显示屏或硬盘驱动器所产生的电磁辐射,来读取你所显示的画面和磁盘内的文件信息。

常量时间编程确保程序执行不依赖于密码

它是通过三条规则来保证这一点的:

  • 控制流(如条件分支)不应该依赖密码
  • 内存访问模式(如进入数组的偏移量)不应该依赖密码
  • 密码不应该被用作可变延迟指令的操作数(如浮点指令或许多处理器上的整数除法的操作数)

这些规则确保机密信息在攻击者强大到足以执行缓存攻击、通过分支预测器状态泄露数据或通过端口争用来窥探数据时仍然安全

但是对于幽灵漏洞,常量时间编程不足以保证数据安全

这是一段易受幽灵攻击的代码

image

如果数组arrA只包含公共数据,并且i和arrAlen也是公共数据,那么这段代码符合常数时间编程,但是攻击者可以利用分支错误预测,通过数据缓存泄露得到秘密数据

攻击者首先用适当小的i值重复运行代码,从而使分支预估调节i<arrAlen为真,然后攻击者为i提供一个越界值,处理器判断条件仍为真,并推测地将越界数据加载到x中,随后它将x值作为内存读取操作地址的一部分,再将x的值编码到数据缓存状态中,根据x的值,不同的缓存行将被访问和缓存,一旦处理器发现了预测错误,就会回滚执行,但是数据缓存状态仍然存在,攻击者可以在以后释放数据缓存状态,以便推断x的值

这段代码,攻击者可通过控制流泄露秘密数据

image

x作为分支条件的一部分,攻击者可以随机将任意内存读取到x中,然后他们可以通过几种方式泄露x的值,比如说根据各种情况的不同执行时间,通过数据缓存,基于在各种情况下执行的不同内存访问,通过端口竞争,分支预测器状态

使用幽灵打破软件隔离

幽灵攻击破坏了软件隔离领域的重要保障,在软件隔离领域内,主机应用程序执行不受信任的代码时,确保不受信任的代码无法访问主机的任何数据,但是幽灵攻击可以破坏内存安全和隔离机制

这张图就是一个幽灵攻击破坏软件隔离的例子

image

在该代码中,攻击者使用函数guest_func()调用宿主函数get_host_val()从数组中获取值

虽然get_host_val()实现了边界检查,但是攻击者仍然可以通过误操作分支预测其来随机性地访问越界数据,从而破坏隔离保证,一旦攻击者获得了他们选择的越界值,他们可以泄露该值,比如说通过数据缓存。我们需要确保,即使是随机性的,不受信任的代码也不能打破隔离

语义学的选择

精心设计幽灵分析工具的基础是精心构造的形式化语义,因此语义学的选择是非常重要的

加密代码需要不同的安全属性,因此需要不同的语义和工具

实用语义应该在细节和抽象之间做出适当的权衡:

  • 它应该足够详细,以捕捉我们感兴趣的微架构行为
  • 它应该足够抽象,以适用于所有合理的硬件,因为我们不希望我们代码的安全性依赖于特定的缓存替换策略或分支预测器的实现
A 泄露模型

泄露模型指攻击者可以观察到什么信息

任何意图建模侧信道攻击的语义都需要精确定义其攻击者的模型,泄露模型是一个语义攻击者模型的重要部分

B 保密策略

确定了泄露模型,我们要确定用什么保密策略,即那些值可以泄露,哪些值不能泄露

密码学和隔离等领域已经定义了顺序安全属性的策略

对于密码学,包含秘密数据的内存被认为是敏感的,比如说加密密钥的内存

隔离是声明程序指定的沙盒区域之外的所有内存都不应该被泄露

沙盒是一种安全机制,为运行中的程序提供隔离环境,提供作为一些来源不可信,具有破坏力或无法判定程序意图的程序使用,沙盒可以提供用后即收回的磁盘及内存空间

C 执行模型

为了推断幽灵攻击,语义必须能够推断推测执行模型中敏感数据的泄露,推测执行模型是推测语义与标准顺序分析的区别,并决定抽象处理器可以执行什么推测,对于开发人员来说,需要权衡选择一个合适的推测执行模型

  • 要选择他们模型允许的行为,比如说它们包含哪些微架构预测器,这决定了它们的工具可以捕获哪些幽灵变体
  • 如果可以执行的推测过多,会使它们的分析更加复杂
D 非确定性

推测执行本质上是具有不确定性的,程序中的任何给定的分支都可能正确或错误地进行,而不管实际的条件值,也就是说推测性劫持攻击可以将执行发送到完全不确定的位置

现有的语义都假设攻击者对微架构预测和调度有完全的控制

限制非确定性:

目前已有研究人员实现用编译器完全重构程序,在软件层加强它们自己的限制行为集,将敏感数据分配到单独的内存区域中,并使用页面权限位来禁止不受信任的代码访问这些区域,无论程序如何推测错误,它都读取不了敏感数据

E 高级别的抽象

幽灵攻击和推测执行从根本上打破了我们对程序应该如何执行的直觉假设,关于程序的高级别的保证不再适用

为了重建更高级别的安全保障,我们首先需要修复我们程序如何执行的模型,从低级语义开始,一旦这些基础牢固地建立了起来,我们才能重建更高级别的抽象

有了足够的基础,我们可以通过修改高级语言的编译器,产生安全的低级程序来恢复高级抽象

F 表现力

一个好的实用的语义需要有足够强的表达性,我们想要一个能够表达的语义对广泛的可能行为进行建模,比如说幽灵变体

另外,允许太多可能行为的语义会使很多分析变得难以处理

而语义的基本目的是提供硬件的合理抽象或简化,来简化分析,因此语义中包含多少表现力也需要认真权衡

结论

推测分析的坚实基础需要对语义和攻击者模型进行适当的选择,开发人员需要考虑较强的泄露模型,那些只通过内存或数据缓存来捕获泄露的泄露模型会导致安全性保证更弱

其次必须考虑所有的幽灵变体,这会增加分析的复杂性,但是开发人员可以将分析与结构化编译技术结合,通过构造来限制或删除幽灵攻击的整个类别

最后,我们建议不要对不必要的架构细节进行建模,比如说对缓存结构或端口争用这种细节进行建模,复杂性更高,并且会降低移植性