4

在 Spectre论文中,有一个利用越界数组访问的示例(第 1.2 节)。代码是

if (x < array1_size)
  y = array2[ array1[x] * 256 ];

该过程是用一些有效值训练正确的路径x。然后给出一个无效的值,x同时假设arra1_size是未缓存的。由于分支预测器认为条件为真,它会推测性地获取 array2 的偏移量。

现在,问题来了。在推测执行中,它必须获取array1[x]x 是恶意的并且超出范围的位置。所以,array1[x]其实是无效的!那么攻击是什么?!没有获取有效数据!

谁能为我解释一下?这里有什么误解?

4

3 回答 3

4

所以,array1[x] 实际上是无效的!那么攻击是什么?!没有获取有效数据!

这是攻击的重点。索引(即x)可能很大,所以我们可以访问我们不应该访问的数据。

例如,如果我们的代码在 JavaScript 沙箱或 Java 虚拟机中,我们将能够访问沙箱/虚拟机之外的数据。

更重要的是,推测执行可能会访问内核页面,即我们无权访问的页面。那就是熔断。

这是我的基于 Spectre 的 Meltdown 概念证明,仅 99 行,您可能会发现更容易理解:

https://github.com/berestovskyy/spectre-meltdown

于 2018-01-31T10:56:13.560 回答
1

Spectre(与 Metldown 不同)得益于 CPU 处理分支预测的方式。从您所指的同一篇论文中

[2.3] 推测执行要求处理器对分支指令的可能结果进行猜测。更好的预测通过增加可以成功提交的推测执行操作的数量来提高性能。
(...)
为了预测是否采用条件分支,处理器会保留最近分支结果的记录。

然后

x[4] 代码片段以对安全性至关重要的边界检查开始。特别是,此检查可防止处理器读取array1. 否则,越界输入x可能会触发异常或可能导致处理器通过提供x = (address of a secret byte to read)−(base address of array1).

但是这篇论文继续解释了为什么这可能会起作用,而不是触发异常:

不幸的是,在推测执行期间,边界检查的条件分支可能会遵循不正确的路径。例如,假设一个对手导致代码运行如下:

• 的值x被恶意选择(并且越界),从而array1[x]解析为受害者内存中某处的秘密字节k ;

array1size 并且array2不存在于处理器的缓存中,但k被缓存;和

• 先前的操作接收到的值x是有效的,导致分支预测器假设if 可能为真。

最后,CPU,在x值被评估为过高后,将有效地绕过if主体,不会撤销检索到的y值。但是缓存状态发生了变化,这就是攻击发生的地方:

处理器意识到其推测执行是错误的,并回退其寄存器状态。但是,在实际处理器上,推测性读取array2以特定于地址的方式影响缓存状态,其中地址取决于k

未经授权的访问array1[x]是在推测代码执行期间进行的,因此不会引发异常,因为 CPU“知道”如果之前分支的条件恰好为假,那么推测执行的结果将不会退出。

(与 Meltdown 不同,它会在执行的用户代码确实访问未经授权的区域时触发异常 - Meltdown 利用退出异常所花费的时间与在该访问之后预先执行的少数无序指令之间的竞争条件(以及不会退休))。

于 2018-01-10T06:54:29.187 回答
0

我的理解(简化)(可能是错误的):

当 x 高于边界时,处理器将加载存储在其他地方的数据。(有点像缓冲区溢出)

例如,当像这样保存数据时:

1 <- 开始数组

2

3 <- 启动秘密数据

4

如果 x 大于它可以读取的范围,例如 3(在论文中,这可能是所有内容及其命名为 k)。然后 3 被缓存

3 用于加载第二个数组中的数据。处理器意识到错误并将其从缓存中删除(3/ k)。

然后 Attaker 可以用不同的方法撤销这 3 个(或在论文 k 中)。例如尝试一个数字并测量加载时间,然后从头开始重复所有内容

(抱歉英语不好)

于 2018-01-08T11:00:14.963 回答