- 无服务器应用面临冷启动开销大的问题;热启动方法可以减少初始化开销,但是占用宝贵内存或者面临有漏洞的运行时威胁。
- 本文方案:重置飞地
C1:通过快照和倒带重置
- 捕获整个 enclave 的内存会使内存占用量增加一倍并减慢倒带速度。挑战在于找到一个点,在该点上,快照中包含的内存的必要部分最少。
- 在大多数系统中加载二进制代码时,二进制代码本身是写保护的。认为代码页目前保持不变,安全区内存中唯一更改的部分是数据(全局变量)、堆栈和堆。这些部分构成了将要倒带的快照。
- 堆和堆栈未被使用,并且在 enclave 启动后立即保持空。在堆和堆栈尚未使用时快照,可以将快照最小化到仅安全区的数据部分。
- 快照过程涉及创建全局变量初始值的备份。所有数据都存储在 .data 和 .bss 段中,在 enclave 中创建一个特殊的缓冲区存储快照。
- 倒带过程包括恢复堆栈、堆和快照。堆栈和堆应该是空的,清零。将快照复制回这两个区段,enclave 将重置为其初始状态,并准备好重复使用。开销实质上涉及内存复制,比 enclave 启动过程快得多。
C2 :嵌套证明
- 执行工作负载之前,每个用户都必须可验证重置并进行代码数据的完整性检查
- 嵌套证明模块的软件模块来执行证明,可以使用 enclave 硬件的远程证明来构建与嵌套证明模块的安全通信通道。
- 每次发生重置时,用户都可以质疑重置是否成功。飞地必须能够证明其余部分已经发生。飞地所有者可以通过验证增加的随机数和匹配的哈希值来确认重置已成功执行。
C3:多层飞地内分区
- 虽然具有嵌套证明的快照和倒带技术可以快速且可验证地重置 enclave,但确保此类技术的安全性并非易事:高级语言运行时的解释器可能有错误,恶意工作负载可能修改快照,嵌套证明必须能够判断重置是否正确执行以及快照是否正确、
- 为了解决这个问题,我们提出了使用编译器技术的多层飞地内分区化(MLIEC)。MLIEC 将安全区地址空间划分为多个安全层。使用较高安全层运行的线程可以访问较低安全层的数据,反之则不能。嵌套证明模块可以放置在高于重置模块的另一层中,以保证证明结果的可信度
基于 SFI 的隔区
软件故障隔离 (SFI) 旨在防止来自较低安全层的代码访问属于较高安全层的数据。对于 SFI,我们采用了 SGX-Shield 的Shepherded Memory Access (SMA.和Aligned Branching。
SMA 机制检测二进制文件中的所有内存访问,以确保访问发生在边界之上。在二进制文件中,有两种类型的内存访问:固定地址和间接地址。间接存储器访问会取消引用寄存器中的地址。因此,间接内存访问可能会被一段恶意代码劫持。专注于间接内存访问,目标是强制所有间接内存访问发生在边界寄存器之上。
通过将这些地址转换为与边界相关的偏移量,并通过快速按位运算强制这些偏移量为正值来检测这些间接内存访问。我们保留了 R15 来存储边界。将地址复制到 R14 中,然后用 R15 减去它以获得偏移量。良性内存访问将获得与操作前相同的地址,而恶意访问将获得边界上方的不同地址,从而导致读取边界上方的随机位置
只要寄存器本身高于边界,堆栈访问就是安全的。例外情况是调用指令,这些指令可能会向下移动堆栈指针,并且如果它移动到边界以下,可能会带来安全风险。
可以很容易地观察到,SMA代码必须作为一个整体执行才能实现安全保证。但是,间接分支可能会违反这种保证。通过重写寄存器,攻击者可以劫持控制流并绕过 SMA 或执行更危险的操作。
为了解决这个问题,我们将代码发送到具有固定大小对齐单元的块中,并强制将检测指令与检测代码发出到同一个块中。检测每个间接分支,使其目标与对齐单元对齐。这可确保强制执行 SFI 检测,如图a所示。
另一种典型的控制流劫持攻击是面向返回的编程 (ROP),通过修改返回存储在堆栈上的指针,使代码分支到任意位置。如图 4b 所示。由于每个返回目标都将引导一个对齐的新基本块,因此返回将按预期工作。
未对齐的关键函数
安全性:对于某些安全关键指令,检测将不足。对于包含授予页面 RWX 权限或设置/转换边界的指令的函数,需要进行全面的安全检查。但是,这些检查可能很长,可能不适合对齐单元,因此即使使用对齐分支技术进行保护,它们也容易被绕过。
性能:对于某些经常使用但占用大量内存的函数(例如 memcpy),检测它们可能会导致性能显著下降。虽然可以手动修改它们以进行安全检查,但检查必须与整个函数一起执行,以保证安全性。因此,与包含安全关键指令的函数类似,仅检测不足以保护这些函数。
对于这些函数,我们的目标是将它们作为一个整体执行,而不会有任何被劫持的代码跳到中间。可以应用控制流完整性 (CFI) 技术来实现这一点。
传统上,CFI是通过捕获和检查来实现的,这会带来明显的性能损失。
通过在相反的方向上应用对齐分支的属性来设计一种快速 CFI 机制。关键观察是,如果我们“不对齐”地发出这些函数,一段恶意代码将无法劫持进入它们的控制流。我们将这种技术称为未对齐的临界函数技术。
我们不进行捕获和检查,而是在这些函数中每个块的代码之前发出 ud2 指令来捕获错误,这将导致 #UD 错误并导致执行崩溃。因此,任何尝试对这些块执行间接分支的检测代码都将命中 ud2 并崩溃。对于具有多个基本块的函数,我们可以使用直接JMP指令将它们链接在一起。确保这些关键功能必须作为一个整体来执行。任何代码都不能被劫持到中间,并且无法绕过它们的安全检查。
多层隔区
如上所述,常规代码、重置模块和嵌套证明模块需要两个以上的安全层。我们提出了一种多层分隔技术,以支持单个飞地内的多个安全层。
动态加载代码
实现与评估
实现
评估
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达,可以在下面评论区评论