13

我理解论文中他们欺骗 CPU 以推测性地将受害者内存的一部分加载到 CPU 缓存中的部分。我不明白的部分是他们如何从缓存中检索它。

4

3 回答 3

10

他们不直接检索它(超出范围的读取字节不会被 CPU “引退”,并且攻击者在攻击中看不到)。

攻击向量是一次进行一点“检索”。在准备好 CPU 缓存(在必须的地方刷新缓存)并且已经“教导”如果条件依赖于非缓存数据时会通过if分支,CPU 会推测性地执行来自if范围,包括越界访问(给出一个字节 B),然后立即访问某个索引处的授权非缓存数组,该索引取决于在秘密 B 的一位上(攻击者永远不会直接看到 B)。最后,攻击者从比方说用 B 位计算的索引中检索相同的授权数据数组,比如零:如果该 ok 字节的检索速度很快,则数据仍在缓存中,这意味着 B 位为零。如果检索(相对)慢,CPU 必须在其缓存中加载 ok 数据,这意味着它没有更早,这意味着 B 位是 1。

例如Cond,所有ValidArray未缓存的,LargeEnough足够大,以确保 CPU 不会一次性加载两者ValidArray[ valid-index + 0 ]并在其缓存中加载ValidArray[ valid-index + LargeEnough ]

if ( Cond ) {
   // the next 2 lines are only speculatively executed
   V = SomeArray[ out-of-bounds-attacked-index ]
   Dummy = ValidArray [ valid-index + ( V & bit ) * LargeEnough ]
}

// the next code is always retired (executed, not only speculatively)

t1 = get_cpu_precise_time()
Dummy2 = ValidArray [ valid-index ]
diff = get_cpu_precise_time() - t1

if (diff > SOME_CALCULATED_VALUE) {
   // bit was its value (1, or 2, or 4, or ... 128) 
}
else {
   // bit was 0
}

依次尝试wherebit是 first 0x01, then 0x02... to 0x80。通过测量“下一个”代码为每个位花费的“时间”(CPU 周期数),可以得出 V 的值:

  • 如果ValidArray[ valid-index + 0 ]在缓存中,V & bit0
  • 否则V & bitbit

这需要时间,每个位都需要准备 CPU L1 缓存,尝试多次相同的位以尽量减少时序错误等......

然后必须确定正确的攻击“偏移”以读取感兴趣的区域。

巧妙的攻击,但并不那么容易实施。

于 2018-01-07T08:05:44.073 回答
4

他们如何从缓存中检索它

基本上,推测检索到的秘密会立即用作索引,以从另一个名为side_effects. 我们所需要的只是“触摸”side_effects数组中的一个索引,因此相应的元素从内存中获取到 CPU 缓存:

secret = base_array[huge_index_to_a_secret];
tmp = side_effects[secret * PAGE_SIZE];

然后测量访问数组中每个元素的延迟side_effects并与内存访问时间进行比较:

for (i = 0; i < 256; i++) {
    start = time();
    tmp = side_effects[i * PAGE_SIZE];
    latency = time() - start;
    if (latency < MIN_MEMORY_ACCESS_TIME)
        return i; // so, thas was the secret!
}

如果延迟低于最小内存访问时间,则元素在缓存中,因此秘密是当前索引。如果延迟很高,则元素不在缓存中,因此我们继续测量。

所以,基本上我们不直接检索任何信息,而是在推测执行期间接触一些内存,然后观察副作用。

这是 99 行代码中基于 Spectre 的 Meltdown 概念证明,您可能会发现其他 PoC 更容易理解: https ://github.com/berestovskyy/spectre-meltdown

一般来说,这种技术称为旁道攻击,更多信息可以在维基百科上找到:https ://en.wikipedia.org/wiki/Side-channel_attack

于 2018-02-03T11:44:55.387 回答
3

我想为已经存在的答案提供一条信息,即攻击者如何在探测阶段从受害者进程中实际探测一个数组。这是一个问题,因为 Spectre(与 Meltdown 不同)在受害者的进程中运行,即使通过缓存,攻击者也不能只从其他进程中查询数组。

简而言之:对于 Spectre,FLUSH+RELOAD 攻击需要 KSM 或其他共享内存方法。这样,攻击者(据我所知)可以在他自己的地址空间中复制受害者内存的相关部分,从而能够查询缓存以获取探针阵列上的访问时间。

长解释:

Meltdown 和 Spectre 之间的一大区别是,在 Meltdown 中,整个攻击都在攻击者的地址空间中运行。因此,很清楚攻击者如何既可以更改缓存又可以同时读取缓存。然而,对于 Spectre,攻击本身在受害者的进程中运行。通过使用所谓的小工具,受害者将执行将秘密数据写入探针数组索引的代码,例如使用a = array2[array1[x] * 4096].

已在其他答案中链接的概念证明实现了 Spectre 的基本分支/推测概念,但所有代码似乎都在同一进程中运行。array2因此,写入小工具代码然后读取array2以进行探测当然是没有问题的。然而,在现实世界的场景中,受害进程将写入array2也位于受害进程中的对象。

现在,问题——我认为这篇论文没有很好地解释——是攻击者必须能够探测缓存中的受害者地址空间数组(array2)。从理论上讲,这可以再次从受害者内部完成,也可以从攻击者地址空间完成。

原始论文只是模糊地描述了它,可能是因为作者很清楚:

在最后阶段,敏感数据被恢复。对于使用 Flush+Reload 或 Evict+Reload 的 Spectre 攻击,恢复过程包括定时访问被监控的缓存行中的内存地址。

为了完成攻击,攻击者测量array2 中的哪个位置被带入缓存,例如,通过Flush+Reload 或Prime+Probe。

从受害者的地址空间中访问缓存array2是可能的,但它需要另一个小工具,并且攻击者必须能够触发该小工具的执行。这对我来说似乎很不现实,尤其是在 Spectre-PHT 中。

在论文Detecting Spectre Attacks by identify Cache Side-Channel Attacks using Machine Learning中,我发现了我缺失的解释:

为了使 FLUSH+RELOAD 攻击在这种情况下起作用,必须满足三个先决条件。[...] 但最重要的是 CPU 必须具有启用内核同页合并 (KSM) [4] 或透明页面共享 (TPS) [54] [10] 之类的机制。

如果进程引用相同的物理地址,KSM 允许进程通过将不同的虚拟地址合并到同一页面中来共享页面。因此,它增加了内存密度,允许更有效的内存使用。KSM 最初在 Linux 2.6.32 中实现,默认启用 [33]。

KSM 解释了攻击者如何访问array2通常只能在受害者进程中访问的内容。

于 2020-12-10T16:07:10.113 回答