从CPU漏洞Meltdown&Spectre看侧信道攻击

0x00 前言
2018伊始,两个芯片级漏洞
Meltdown
(熔断)、
Spectre
(幽灵)漏洞震惊的安全界。受影响的CPU包括
Intel
、
AMD
和
ARM
,基本囊括的消费级CPU市场的绝大部分。
Meltdown
漏洞可以在用户态越权读取内核态的内存数据,
Spectre
漏洞可以通过浏览器的
Javascript
读取用户态的内存数据。虽然这两个漏洞对个人PC影响有限,但是确摧毁了公有云的基石——用户可在虚拟机里可以无限制的读取宿主机或者其他虚拟机的数据。
0x01 背景知识
了解
Meltdown
和
Spectre
漏洞之前,首先要知道几个背景知识。现代CPU为了提高运算效率与运算速度,会采用以下的手段提高CPU运算速度:分支预测(
branch prediction
)、推测执行(
speculation execution
)和乱序执行(
out-of-order execution
)。
- 分支预测与推测执行 当包含CPU处理分支指令时就会遇到一个问题,根据判定条件的真/假的不同,有可能会产生跳转。此时CPU不会等待判定结果,而回预测出某一个条件分支去执行。
- 乱序执行 CPU遇到指令依赖的情况时,会转向下条不依赖的指令去执行。
0x02 Meltdown漏洞分析
Meltdown
漏洞允许我们在用户态无限制的读取内核态的数据。我们来看一段代码
; rcx = kernel space address; rbx = user space addressmov al, byte[rcx]shl al, 0xcmov rbx, qword[rbx + rax] |
---|
这段代码看上去并没有什么问题。因为在用户态的时候,在执行第一行代码时就会因为鉴权失败而停止执行后面的代码。
然而,当现代CPU执行这一段代码时,由于之前提到的特性,CPU为了加快运算速度,在执行完第一行代码后,在耗时的鉴权时,会执行第二行、第三行代码。当鉴权失败后,CPU会将状态回滚,当作后面的代码没有执行,此时用户态程序还是没法获取到内核态的数据。
但是,问题来了。CPU状态回滚后,缓存Cache并不会回滚,我们还是可以通过侧信道攻击(
Side Channel Attack
)来猜测内核态的数据。此时,我们还要知道两点:第一,当CPU访问一个地址时,若没有在缓存中则会将这个地址所在的内存页(4KB = 4096B)放入缓存中;第二,访问缓存数据的速度远大于访问内存。
这段代码在第一行取了
kernel address
存放的第一字节的数据,第二行将这个数据左移
0xc
位,相当于乘以
4096
,也就是
4K
。此时,这个数据就相当于一个内存页的
index
序号。第三行代码访问了这个地址,此时CPU会将这个内存页放入缓存中。接下来,我们遍历一下
index
从0到255号内存页,访问特别短的那个内存页的序号就是我们要猜测的数据。
下面,我们通过图解来展示一下这个漏洞的原理。
第一步,将
kernel space address
的第一字节放入
rax
的低
8
位,假设为
0x20
。

第二步,将
rax
的低8位左移
0xc
位,也就是
0x20 * 4096
。
第三步,访问
rbx + rax
也就是
rbx + 0x20 * 4096
,此时会将这个地址所在的内存页放入缓存中。
第四部,遍历
rbx + index * 4096
,若缓存命中,则说明此时的
index
就是
rcx
指向的内核态的第一字节数据。

此时,访问
rbx + 0x20 * 4096
命中缓存,所以
rcx
指向内核态的第一字节数据为
0x20
。
接下来,我们看看
Github
上放出的
PoC
代码https://github.com/paboldin/meltdown-exploit/。因为这个代码同时支持`x86_64`和`i386`,所以核心汇编代码有两段。
- x86_64 asm volatile ("1:\n\t"".rept 300\n\t""add $0x141, %%rax\n\t"".endr\n\t""movzx (%[addr]), %%eax\n\t""shl $12, %%rax\n\t""jz 1b\n\t""movzx (%[target], %%rax, 1), %%rbx\n""stopspeculate: \n\t""nop\n\t":: [target] "r" (target_array),[addr] "r" (addr): "rax", "rbx");