组会纪要

2019-11-22 组会纪要

Posted by 黄鑫 on November 22, 2019

组会-20191122-组会纪要

主题:2019-USENIX-ERIM: Secure, Efficient in-process Isolation with Memory Protection Keys 论文讨论

论文概要


ERIM的研究内容是如何隔离敏感数据以提高应用程序的安全性和健壮性,可以保护像Heartbleed案例中的密钥不被泄露,也可以保护程序语言的运行时安全。ERIM是基于MPK设计的隔离方案,开销比较小,即使权限切换很频繁(每秒100000次切换),ERIM的平均开销仍然小于1%。Linux从4.6版本开始支持MPK,页表项中会有4位用来存储Protection Key。有专门的系统调用(pkey_mprotect)可以为某个页绑定Protection Key。因此最多能将进程的地址空间分为16个域。PKRU存储了每个Key对应页面的具体访问权限。当要改变某个域的访问权限时,只要通过一个用户态的指令WRPKRU(11-260 cycles)即可,不需要系统调用,不需要对页表修改,刷新TLB等。由于WRPKRU是用户态的指令,ERIM需要防止恶意的攻击者利用这个指令来切换权限。

威胁模型


  1. CPU,OS和隔离技术是受信任的。
    • 除了受信任的隔离专区以外,我们不信任该应用程序。特别是,我们允许恶意攻击利用例如不受信任的应用程序中可能存在的内存损坏或控制流劫持漏洞
  2. 侧通道攻击,raw hammer和微体系结构攻击不在范围内。

MPK介绍


Intel的软件开发手册里面的4.6.2节是如下图描述MPK的。简而言之,MPK提供了一种用于执行基于页的保护的机制,当应用程序更改保护域的访问权限时,无需修改页表。它通过将每个页表条目中的4个先前未使用的位专用于“保护键”来工作,从而提供16个可能的键。还有一个新的32位的寄存器(PKRU),每个键具有两个对应的位(访问禁用和写禁用)。作为CPU寄存器,PKRU本质上是线程局部的,有可能为每个线程提供与其他每个线程不同的保护集。有两个新指令(RDPKRU / WRPKRU)用于读取和写入新寄存器。该功能仅在64位模式下可用。这些权限仅在数据访问时强制执行,而对指令提取无效。

  • Because of stray writes, memory corruption is an issue with complex multithreaded applications. For example, not every part of the code in a database application needs to have the same level of privilege. The log writer should have write privileges to the log buffer, but it should have only read privileges on other pages. Similarly, in an application with producer and consumer threads for some critical data structures, producer threads can be given additional rights over consumer threads on specific pages.
  • The page-based memory protection mechanism can be used to harden applications. However, page table changes are costly for performance since these changes require Translation Lookaside Buffer (TLB) shoot downs and subsequent TLB misses. Protection keys provide a user-level, page-granular way to grant and revoke access permission without changing page tables.

核心思想


  1. EIRM将程序分为可信和不可信部分
    • T: 可信部分代码
    • U: 剩余的不可信部分代码
    • M_T: 需要保护的内存
    • M_U: 剩下的不需要保护的内存 ERIM所采取的措施:1. 当U执行时,不允许其访问M_T; 2. 想要访问M_T,需要将程序跳转到T的入口点,在T的出口处再撤销访问M_T的权限。
  2. 从U切换到T需通过call gate

难点与解决方案


  1. 需要解决的难点
    • 防止U利用WRPKRU指令改写访问权限
  2. 二进制检查 ERIM依赖于二进制检查(Binary Inspection)来保证只有安全的WRPKRU出现在可执行页里。二进制检查由检查函数(Inspection Function)和监听(Interception)两部分组成
    • Inspection Function 二进制检查需要扫描一系列的可执行页是否出现WRPKRU指令或者XRSTOR指令。找到后,就会检查是否安全的。如验证指令后面是否跟着用于检查权限的指令。
    • Interception 通过配置Seccomp-bpf Filter来监听mmap、mprotect、pkey_mprotect等系统调用是否将页映射为可执行权限。Seccomp没有直接的方法来监听PKRU,作者是通过SECCOMP_RET_TRACE来通知Tracer。Tracer会允许来自T调用的这类系统调用,对于来自U的就会拒绝。不过ptrace的方式开销很高,但是将页映射为可执行权限这样的操作一般比较少。如果真的很多,也可以通过写一个简单的Linux内核模块来实现。有了这样的监听的存在,U必须要进入T才能映射可执行的页。
  3. 消除无意的WRPKRU指令
    • 重写二进制文件中出现的WRPKRU指令
  4. 如果这个指令跨越了多条指令,只需在这些指令之间插入NOP即可
  5. 如果是在一条长指令的里面,则需要替换为语义相等的指令,或者一系列指令, 对于可以用源码重新编译的情况,重写可以在编译器里完成。如果不能,就要在运行时做二进制重写。

实验


  1. 隔离Web服务器里的密码学密钥。例如Heartbleed这个案例。long-term key不是很频繁地被访问,基本上每个会话就几次。Session key则会更频繁地被访问,每秒超过$10^6$次(NGINX这种高吞吐量的Web服务器)。ERIM可以高效地隔离Session key。作者修改Openssl的libcrypto库,隔离了Session key和基本的密码相关代码。 下图是吞吐量的实验结果,表明在权限切换很频繁的情况下,ERIM的开销仍然很小。
  2. 隔离Native库。例如Java和Javascript VM通常都依赖于第三方的Native库来保证性能,ERIM可以用来隔离这些运行时。 下图显示了开销的实验结果。
  3. 隔离CPI的Safe Region。下图展示了开销的实验结果。
  4. 与现有技术的性能开销比较 下图是实验结果图,可以看到ERIM的运行时性能还是比较好的。

讨论


  1. 有没有可能通过更改页表结构的方式突破ERIM的内存隔离,或者说MPK目前还存在什么安全问题,针对PKRU有没有更好的保护措施?
  2. 攻击者有没有可能直接通过库函数pkey_set函数直接更改PKRU的值,从而改变相应页面的访问权限?作者是否有对glibc作相应的处理?
  3. ERIM是对用户空间进程内部数据有效隔离的一种机制,前提是操作系统和部分应用程序是可信赖的,那么它对比SGX(前提是操作系统都是不可信的,部分应用程序可以信赖),在保护能力上有增强吗?还有,它的效率比SGX高多少?如果需要保护部分敏感数据,对于用户而言,采用SGX好还是ERIM好?
  • 保护能力上来说,应该还是SGX更强,毕竟ERIM利用MPK需要假设OS是安全的,因为PTE一旦被改写,权限控制就乱了。而且更新PKRU寄存器的指令是用户层指令,可能被ROP劫持执行,目前没看到针对PKRU更好的保护措施。性能开销理论上ERIM应该比SGX好一点,具体效率高多少文章没有实验测试,所以不清楚。