科技行者

行者学院 转型私董会 科技行者专题报道 网红大战科技行者

知识库

知识库 安全导航

至顶网安全频道独家解析:Frozen Cache抵御内存威胁

独家解析:Frozen Cache抵御内存威胁

  • 扫一扫
    分享文章到微信

  • 扫一扫
    关注官方公众号
    至顶头条

  Cold boot 攻击对全磁盘加密解决方案所提供的数据安全保护是一个重大威胁,大部分计算机都容易受到这种攻击,而到目前为止还没有通用方法可以解决这个问题。

来源:比特网 2011年5月2日

关键字: 系统安全 安全技术

  • 评论
  • 分享微博
  • 分享邮件

  Cold boot 攻击对全磁盘加密解决方案所提供的数据安全保护是一个重大威胁,大部分计算机都容易受到这种攻击,而到目前为止还没有通用方法可以解决这个问题。

  为什么这篇文章名为“冻结缓存(Frozen Cache)”呢?因为我们决定采用CPU缓存来抵御Cold boot 攻击(至少对于所有X86系统)。

  这个概念很简单:将缓存切换到特殊模式,该模式下可以强迫数据在缓存中而不是被写入支持RAM位置。因此,加密密钥不能从RAM提取,其实这种技术并不新鲜:LinuxBIOS/CoreBoot将这种技术称为Cache-as-RAM,并使用这种技术来允许“RAM访问”,即使是在初始化内存控制器前。

  下列步骤加载并维护CPU缓存中的256位的加密密钥,并假定加密密钥存放在RAM的线性地址X中:

  1. 从RAM将加密密钥加载到一些CPU注册器中(如SSE注册器):

  movq [X], %xmm0

  movq [X+8], %xmm1

  movq [X+16], %xmm2

  movq [X+24], %xmm3

  2. 使用0值复写RAM中加密密钥

  movq 0, [X]

  movq 0, [X+8]

  movq 0, [X+16]

  movq 0, [X+24]

  3. 刷新缓存(从而真正覆盖RAM中的加密密钥)

  wbinvd

  4. 添加需要的RAM区域到CPU的MTRR(4K部分包含密钥)

  movl (X | MEMORY_TYPE_WRITEBACK), %eax

  xorl %edx, %edx

  movl 0x200, %ecx

  wrmsr

  movl ( ~(1) | MTRR_VALID ), %eax

  movl 0x1, %edx

  movl 0x201, %ecx

  wrmsr

  5. 禁用/冻结CPU的缓存(CR0.CD=1)

  movl %cr0, %eax

  orl 0x40000000, %eax

  movl %eax, %cr0

  6. 从CPU注册器向RAM写入加密密钥(数据仍然在缓存中,不会写入内存)

  movq %xmm0, [X]

  movq %xmm1, [X+8]

  movq %xmm2, [X+16]

  movq %xmm3, [X+24]

  “禁用/冻结”CPU的缓存严重降低了性能,但是,这似乎也是可以接受的,因为考虑到只需在屏幕锁定时设置特殊模式(如果笔记本被盗,那么所以努力都毫无意义)。在Linux系统的首次概念证明测试结果表明,需要对解锁GUI进行很多必要的性能优化(从性能/可用性角度考虑)。

  这段文字只是描述了基本的概念,下面将涉及多CPU/Core问题、性能考虑和优化等问题。

  性能方面

  如上所述,性能问题是将缓存切换到“冻结”模式的主要关注问题,CPU架构的广泛范围(多CPU、多核和多线程)和各自的缓存配置让问题变得更加复杂。

  首先:只有一个CPU缓存需要“冻结”以有效保护加密密钥。其他CPU允许执行政策缓存模式。只要每个(逻辑或者虚拟的)CPU完全使用自己的缓存:采用线程技术(如英特尔的超线程HyperThreading)CPU会作为两个(或者可能更多)逻辑CPU,但是这两个CPU共享相同的缓存,而它们必须同时切换至冻结缓存模式。对于多核CPU,可能情况更加复杂,如果内核都有自己的(L1和L2)缓存。

  加密密钥仅位于单个CPU的缓存中,只有这个CPU必须执行加密和解密线程,在全磁盘加密解决方案中最常见的架构就是采用内核模块,它会产生一个为加密和解密逻辑指定的内核线程,内核线程是可调度实体,因此可以绑定在CPU上,CPU缓存中有加密密钥。

  回到更加“传统的”性能考虑,怎样做可以减少冻结CPU缓存带来的影响呢?将最常用的内存区域加载到缓存(冻结前)是个不错的开始,其中最适合的包括:系统调用入口端点、计时器中断线程及其“帮助”功能,以及内核线程执行的加密/解密功能。目前L2缓存通常可以容纳所有这些代码,但是同时还需要考虑缓存的结合问题,以避免相关问题。另一个好办法就是将所有其他进程的时间表分配给其他可用的CPU(不使用冻结缓存的CPU):这样可以以正常的速度执行,另一个很重要的原因就是,我们能获得更多时间。

  现在已经很明显了,将需要部署确定具体(运行中的)CPU/缓存组件的程序并有针对性的管理他们。

  缺乏缓存控制

  一旦缓存被冻结,对这些缓存内容的管理就没有结束:同样重要的是,缓存中的数据(加密密钥)并没有写回内存。不幸的是,英特尔架构只允许非常少的缓存控制:

  · 启用/禁用缓存(系统范围:CR0.CD,每个内存区域:MTRR,每页:PAT)

  · 清除缓存(wbinvd

  就是如此,根本没有查询缓存状态(目前在单个缓存线中的RAM位置)处理器指示或者其他先进的缓存管理功能。因此,根本可能验证加密密钥真正只出现在CPU缓存中。随着冻结缓存设置,几乎可以肯定密钥将出现在缓存中,但是这并不意味着数据将同步到RAM中。不管wbinvd指示何时执行都会发生(在冻结缓存步骤中),该指示可以由任何在ring 0(内核)运行的代码执行。因此,有必要尽量减少在CPU(加密密钥在其缓存中)运行的内核模式,这也是为什么绑定其他可调度实体(至少是所有其他内核线程)到其他CPU(如果有的话)的原因。

  减少“无意的”缓存清除带来的影响的方法是重复缓存冻结程序以减轻“无意”缓存清除(wbinvd)的影响。对于Linux而言有个更好的解决方法:修改执行/包装在内核中触发重新执行缓存冻结的invd/wbinvd指示的函数/宏。

  保护加密密钥

  加密密钥不是唯一需要保存在CPU缓存的数据类型。

  密钥调度是现代密码加密的典型:加密和解秘例程并不直接使用加密密钥,而是采用“轮流密钥”,这些都来自加密密钥(根据密码算法)并且用于不同算法轮次的加密与解密。

  AES标准定义了128位的AES密钥用于生成/获得10轮128位的密钥,可以推测这些轮密钥通过某些不可逆的类似哈希的函数从加密密钥中计算出,不过这对于AES标准是不正确的:加密密钥很容易从任何10/12/14轮密钥中重新计算出。因此,这些轮密钥也需要保存在CPU缓存中(至少对于经常使用的AES是这样的)。

  理论上听起来简单,但找到一个好的架构方法确实是挑战,特别对于Linux系统。

  锁定屏幕

  另一个重要问题就是,只有当屏幕被锁定时才会凸显对性能造成的影响,不过也可能在锁定屏幕的情况下而不受到性能的影响。

  有两个策略可以维持原有系统性能:

  1. 配置选项,确定缓存何时不应该被冻结

  2. 屏幕锁定后的时间窗口允许用户防止缓存冻结以进行用户交互操作等

  配置选项可以是“当系统在充电时不要冻结缓存”或者名为gcc的程序正在运行的时候不要冻结缓存等。

  时间窗口的方法有点像倒数计时,从屏幕锁定开始(用户可能仍然在电脑前)。在倒计时点击“不冻结缓存”可以使密钥安全受到威胁,不点击可以继续保护密钥安全(因此当计算机自动锁定屏幕时,只会增加一个加密密钥的额外窗口询问用户是否冻结缓存)。

  保护所有需要保密的数据

  本来我的建议是将加密密钥从RAM移到CPU缓存中进行保护,然而情况并非如此。另外,轮密钥也需要保存在CPU缓存中。

  很明显,加密密钥需要受到保护,其次,密钥时间表(前文所说的轮密钥)也需要保护,密钥时间表是直接从加密密钥提取的,可以看作是加密密钥的“扩张版”。第三,应该保护“Initialization Vector(IV,初始化向量)”,IV是否需要获得保护取决于IV的生成方式。例如,“Encrypted Salt-Sector Initialization Vector(ESSIV)”就必须获得保护,ESSIV是加密密钥的hash,是Linux系统中dm-crpt默认使用的IV。第四,任何包含解密内容的缓冲都需要保护,以避免已知的纯文本攻击,然而保护这种内存缓冲非常麻烦。最后,在加密解密过程中计算的数据值必须安全地存储在CPU缓存中以避免密钥分析。

  控制失控的缓存

  失控缓存是指不确定CPU缓存中的数据是否被清除到RAM,这种清除可能是由CPU指示(如invd、wbinvd和clflush,或者外部事件等)。

  以下方法可以避免这种失控缓存问题的发生:

  减少无意缓存清除的方法之一就是定期重复缓存冻结程序以消除这种无意缓存清除的影响。

  有个新方法可以将这种风险一起消除,不过还没有证实这种方法是否可以部署。大家有必要理解物理/线性和虚拟内存位置的区别。

  这种新想法其实很简单:将数据存储在物理/线性位置(不是由系统RAM支持的),这能够保证数据不会离开CPU缓存,即使触发缓存清除。

  虽然还没有证实过是否能执行,可能的安装程序如下:

  1. 加载数据到CPU注册器

  2. 重写RAM的数据

  3. 将虚拟地址的虚拟-线性映射更