熔断漏洞原理与解决方案

原理:  

        在现代处理器上,内核和用户进程之间的隔离通常由处理器的一个监督位来实现,该监督位定义是否可以访问内核的内存页面。其基本思想是只有在进入内核代码时才能设置该位,切换到用户进程时该位被清除。

  乱序执行:现在的CPU几乎都是乱序执行的。比如前后并不关联的指令,它是可以乱序执行的,因为前面的指令可能需要等待读取内存,这是需要很多指令周期的。CPU为了不浪费指令周期它可以先执行后面不相关的指令(因此有些地方要使用内存屏障)。

       按理来说攻击者是没办法在用户态读取到其他进程或是内核的内存,因为用户态的进程是隔离的(当然,如果攻击者能把攻击代码写在内核模块中,那么它可以为所欲为了)。 比如攻击者可以写一段jsp代码放在浏览器的沙盒中执行。

1 raise_exception();
2 // the line below is never reached
3 access(probe_array[data * 4096]);

      以上代码中执行过程中出现异常,因为CPU是乱序执行的,它认为这两句代码没什么关联,所以它接下来的代码将有可能能得到部分的执行。要命的是intel处理器在进入特权状态后访问内存的时候居然不进行指令的权限检测。 上面的异常导致CPU进入特权状态,但下面的代码的指令的权限是用户态的。 所以导致内存从cache中泄露(这部分可见幽灵漏洞中解释)。

     内核内存空间是线性映射的。

解决方法:

首先,这并不是一个软件的漏洞,而是硬件的问题。我们来讨论几种方案:

1. 完全禁止乱序执行: 这不太可行,这对性能是毁灭性的打击。

2. 序列化的权限检测:就是每次取内存的时候都检查一下权限,但是这样性能消耗也非常大,也不太可行。

3. 通过物理内存隔离来实现:这可以通过现代内核使用CPU控制寄存器(例如CR4)中的新的硬分离位来启用。如果hardsplit位被设置,内核必须驻留在地址空间的上半部分,用户空间必须驻留在地址空间的下半部分。通过这种硬分割,内存提取可以立即识别目标的这种提取是否会违反安全边界。 现在的物理内核中用户态和内核态并非隔离的,而是杂乱的排在一起。 弃用硬隔离后两者的物理地址空间直接隔离,那么检测是否有违反安全边界就很简单了。

新的内核采用第三种方案,也就是KAISER,在Linux正式版本中叫做KPTI。 可能是将用户态进程的内核栈从内核的线性映射改为非线性映射,类似于用户态。 但是用户态和内核态的栈必须隔离,安全起见。 所以还需要一些工作就是防止CR4切换带来的cache失效。

posted @ 2018-01-15 10:04  你的KPI完成了吗  阅读(2597)  评论(0编辑  收藏  举报