首页
社区
课程
招聘
[原创]浅谈侧信道时序攻击【附Demo】
2018-1-12 22:25 11584

[原创]浅谈侧信道时序攻击【附Demo】

bxc 活跃值
6
2018-1-12 22:25
11584
最近快被Meltdown与Spectre这两个CPU漏洞刷了屏了,于是有空自己写了个demo玩玩。
虽然没能成功演示上面的两个漏洞,不过大概搞清楚了利用原理。
Meltdown与Spectre 成功获取受保护数据的方式,目前据我所知,好像都是基于侧信道时序攻击的。
不同的是 Meltdown是利用CPU的乱序执行泄漏数据,而Spectre是利用分支预测泄漏数据。
泄露的手段就是读取一个byte,然后以byte为数组索引,访问某个大数组的元素。
然后逐个测试各个元素的访问速度,之前访问过的元素,如果还在缓存中,访问起来就要快不少。
废话不多说,上代码:
#include <intrin.h>
#include <stdio.h>
#include <Windows.h>

#define SHIFT_NUMBER          0x0C
#define PAGE_SIZE             4096
#define BLOCK_SIZE            (1 << SHIFT_NUMBER)


void CacheLineFlush(LPBYTE lpArray, UINT index) {
  _mm_clflush(&(lpArray[index << SHIFT_NUMBER]));
}

void CacheLineFlush_all(LPBYTE lpArray) {
  for (UINT i = 0; i < 256; i++) CacheLineFlush(lpArray, i);
}

/************************************************************************/
/* 统计各个块的访问速度,并返回最快的那个块的索引                       */
/************************************************************************/
BYTE GetAccessByte(LPBYTE lpArray) {
  UINT64 speed[256];
  UINT64 start, min;
  UINT index, junk;
  BYTE result;

  //为min赋初始值
  min = 0;

  //测试访问速度
  for (int i = 0; i < 256; i++) {
    //获取array[index]的地址
    index = i << SHIFT_NUMBER;
    //mfence指令用于序列化内存访问,即让乱序执行无效化。
    //后面的指令必须在前面的内存读写完成后再开始发射执行。
    _mm_mfence();
    //记录开始周期
    start = __rdtscp(&junk);
    junk = *(LPDWORD)(&lpArray[index]);
    _mm_mfence();
    speed[i] = __rdtscp(&junk) - start;

    //如果是初始值,或者比当前值还小
    if ((min == 0) || (speed[i] < min)) {
      min = speed[i];
      result = (BYTE)i;
    }
  }

  return result;
}

/************************************************************************/
/* 用index作为数组索引,访问array的某个元素                             */
/************************************************************************/
BYTE AccessArray(LPBYTE lpArray, UINT index) {
  return lpArray[index << SHIFT_NUMBER];
}


int main()
{
  //假定的kernel内存
  BYTE kernel[4];
  //array是用户可控制的内存
  LPBYTE array;

  kernel[0] = 0x55;
  kernel[1] = 0xAA;
  kernel[2] = 0xF0;
  kernel[3] = 0x0F;

  array = (LPBYTE)_aligned_malloc(256 * BLOCK_SIZE, PAGE_SIZE);

  /*
  实现原理:
    kernel假定是受保护的内存数据 (本demo里可访问)。
    array为用户可控制的一个数组,一共分为256个块,每个块的大小为2的整数倍: (1 << SHIFT_NUMBER)。
    然后从kernel中读取一个byte,以这个byte为索引,去访问array所对应的块。
    之后立刻循环读取一遍array的各个块,如果之前访问成功了,那么对应的块应该还在缓存中,对应的访问时间要少很多。
    统计各个块的访问周期数,最快的块,他的索引就是受保护的那个byte。

    CacheLineFlush_all 函数用于把整个数组从缓存中清除出去,这样不至于污染访问速度。
    AccessArray 函数用于以一个索引去访问一个数组。
    GetAccessByte 函数用于测试数组的各个块的访问速度,返回最快的那个块的索引。
  */


  CacheLineFlush_all(array);
  AccessArray(array, kernel[0]);
  printf("Access fastest: 0x%02X\n", (DWORD)GetAccessByte(array));

  CacheLineFlush_all(array);
  AccessArray(array, kernel[1]);
  printf("Access fastest: 0x%02X\n", (DWORD)GetAccessByte(array));

  CacheLineFlush_all(array);
  AccessArray(array, kernel[2]);
  printf("Access fastest: 0x%02X\n", (DWORD)GetAccessByte(array));

  CacheLineFlush_all(array);
  AccessArray(array, kernel[3]);
  printf("Access fastest: 0x%02X\n", (DWORD)GetAccessByte(array));

  getchar();

  return 0;
}



以上代码,目前只有编译为Debug版本,可以成功演示,Release版本估计把不少关键点给优化掉了。

[CTF入门培训]顶尖高校博士及硕士团队亲授《30小时教你玩转CTF》,视频+靶场+题目!助力进入CTF世界

上传的附件:
收藏
点赞1
打赏
分享
最新回复 (8)
雪    币: 292
活跃值: (680)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
Keoyo 2 2018-1-13 12:40
2
0
收藏,感谢分享!
雪    币: 216
活跃值: (250)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
tsoo 2018-1-15 09:18
3
0

"然后从kernel中读取一个byte,以这个byte为索引,去访问array所对应的块。

之后立刻循环读取一遍array的各个块,如果之前访问成功了,那么对应的块应该还在缓存中,对应的访问时间要少很多。"


你这个例子中的kernel是R3地址, 这个逻辑能执行通.


但如果kernel真的是一个kernel地址 , 比如 BYTE* kernel = 0xfffff80004888f30 ,  在执行kernel[0]  , kernel[1] 的时候就异常了,  后面的"立刻循环读取一遍array..." 也就执行不了.


我试过在执行kernel[0]的时候加_try,   但这样就不能重现了.

雪    币: 7076
活跃值: (3468)
能力值: ( LV12,RANK:340 )
在线值:
发帖
回帖
粉丝
bxc 6 2018-1-15 19:14
4
0
tsoo "然后从kernel中读取一个byte,以这个byte为索引,去访问array所对应的块。之后立刻循环读取一遍array的各个块,如果之前访问成功了,那么对应的块应该还在缓存中,对应 ...
所以说,本文只是验证侧信道时序攻击,不验证Meltdown与Spectre漏洞。
雪    币: 239
活跃值: (650)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
只是来打酱油 2018-1-17 09:41
5
0
Access  fastest:  0x04
Access  fastest:  0x5B
Access  fastest:  0xF0
Access  fastest:  0x0F
雪    币: 310
活跃值: (1917)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
niuzuoquan 2018-1-23 20:17
6
0
mark
雪    币: 290
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
今天不吃面 2018-1-25 09:14
7
0
tsoo "然后从kernel中读取一个byte,以这个byte为索引,去访问array所对应的块。之后立刻循环读取一遍array的各个块,如果之前访问成功了,那么对应的块应该还在缓存中,对应 ...
用分支预测,这样if里面的越权取值即使不执行,数据也会缓存
雪    币: 268
活跃值: (114)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
sushuangju 2018-1-30 10:34
8
0
确实,看了好久,才大致明白了代码的原理。
1. 通过比较访问时间窃取了受保护的数据,也被称为侧信道攻击;
2. 由于CPU预执行的存在,受保护的数据被预执行指令加载到cache中,进而使得对受保护数据的访问速度更快;
3. 本例中,假设我们无法直接访问kernel数组;也就是说,我们无法调用AccessArray函数。但是, 由于CPU指令预测机制的存在 ,我们在执行该函数之前,array数组中kernel数组元素作为下标对应的数组元素会被加载到cache中。然后,我们执行GetAccessByte函数的时候会发现,这些数组元素的访问速度会更快。我们进而可以间接获得数组下标,进而得到kernel数组所有元素的值。
其实,理论上一点不复杂。但这个思路确实很特别,让人耳目一新啊。

雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
VXdulei8772 2020-6-22 10:55
9
0
写的很好,可以认识一下吗?我们也是专门搞侧信道及物联网安全攻击技术的,我的微信dulei8772
游客
登录 | 注册 方可回帖
返回