2018年,CFAR团队人员曾发表文章讨论CFAR计划,现在摘译部分内容如下,以供参考。
一、使用 DARPA的CFAR保护软件免受利用
今天,我们将讨论作为DARPA网络容错攻击恢复 (CFAR) 计划的一部分,我们正在解决的一个难题是:自动保护软件免受 0-day攻击、内存损坏和许多当前未发现的错误的影响。你可能会想:“我不能只使用堆栈保护、CFG或CFI等漏洞缓解技术来编译我的代码吗?”这些缓解技术很好,但需要源代码和对构建过程的修改。在许多情况下,更改构建过程或更改程序源代码是不可能或不切实际的。这就是为什么我们的CFAR解决方案可以保护源不可用或不可编辑的二进制安装。
CFAR非常直观且看似简单。该系统并行运行软件的多个版本或“变体”,并使用这些变体之间的比较来确定一个或多个在行为上何时与其他不同。这个想法类似于入侵检测系统,它将程序行为与在相同输入上运行的自身变体进行比较,而不是与过去行为的模型进行比较。当系统检测到行为分歧时,它可以推断出发生了一些不寻常的、可能是恶意的事情。
像所有DARPA项目一样,CFAR是一个大而困难的研究问题。我们只研究其中的一小部分。我们已经与我们的队友——Galois、Immunant等——谈过了这篇文章,他们每个人都有更多关于他们各自对CFAR项目的贡献的详细信息。
我们很高兴谈论CFAR,因为我们的工具之一McSema是我们团队基于LLVM的多功能解决方案的一部分。作为这篇文章的一部分,我们将展示鲜为人知的 McSema功能的示例,并解释为什么开发它们。我们还将展示如何使用McSema和UCI多编译器来强化现成的二进制文件以防止被利用。
- 我们的CFAR团队
CFAR的总体目标是在不影响核心功能的情况下检测现有软件中的故障并恢复。我们团队的职责是生成一组最佳变体,来减少检测引发故障的输入。其他团队负责专门的执行环境、红队演习等。Galois公司关于CFAR的文章更详细地描述了该程序。
变体之间的行为必须与原始应用程序相同,并提供令人信服的证据,证明所有有效输入的行为都将保持相同。我们的队友已经开发了转换,并为具有可用源代码的程序提供了等效保证。该团队使用Clang/LLVM工具链设计了一个基于多编译器的变体生成解决方案。
- McSema的角色
我们一直致力于生成纯二进制软件的程序变体,因为专有或旧应用程序可能无法获得源代码。我们团队基于源代码的工具链工作在LLVM中间表示(IR)级别。在IR级别转换和强化程序允许我们在不更改程序源代码的情况下操纵程序结构。使用McSema,我们可以将纯二进制程序转换为LLVM IR,并将相同的组件重新用于源级和纯二进制变体生成。
CFAR准确翻译程序需要我们弥合机器级语义和程序级语义之间的鸿沟。机器级语义是由单个指令引起的处理器和内存状态的变化。程序级语义(例如,函数、变量、 try/catch 异常块)是表示程序行为的更抽象的概念。McSema被设计为机器级语义的翻译器(“McSema”这个名字来源于“机器代码语义”)。然而,为了准确转换 CFAR 所需的变体,McSema还必须恢复程序语义。
我们正在积极努力恢复越来越多的程序语义,并且已经支持许多常见的用例。在下一节中,我们将讨论如何处理两个特别重要的语义:堆栈变量和全局变量。
- 堆栈变量
编译器可以将数据支持函数变量放置在多个位置之中。程序变量最常见的位置是堆栈,这是一个专门用于存储临时信息的内存区域,调用函数可以轻松访问。编译器存储在堆栈上的变量称为堆栈变量。
当攻击者将错误转化为漏洞利用时,他们通常依赖于堆栈变量的特定顺序。多编译器可以通过生成程序变体来解决此类漏洞,其中没有两个变体具有相同顺序的堆栈变量。我们想为二进制文件启用此堆栈变量混洗,但有一个问题:在机器代码级别没有堆栈变量的概念。相反,堆栈只是一个大的连续内存块。McSema切实地模拟了这种行为。
- 堆栈变量恢复
将表示堆栈的内存块转换为单个变量的过程称为堆栈变量恢复。McSema将堆栈变量恢复实现为一个三步过程。
1、 McSema在反汇编过程中通过反汇编程序(例如,IDA Pro )启发式和基于DWARF的调试信息识别堆栈变量边界。之前有关于在没有此类提示的情况下识别堆栈变量边界的研究,我们计划在未来使用。
2、 McSema尝试识别程序中的哪些指令引用了哪个堆栈变量。必须准确识别每个引用,否则生成的程序将无法运行。
3、 McSema为每个恢复的堆栈变量创建一个LLVM级变量,并重写指令以引用这些LLVM级变量,而不是先前的整体堆栈块。
堆栈变量恢复适用于许多功能,但并不完美。当McSema遇到具有以下特征的函数时,它将默认采用将堆栈视为整体块的行为:
1、可变参数函数。使用可变数量参数的函数(如常见的printf 系列函数)具有可变大小的堆栈帧。这种差异使得很难确定哪些指令引用了哪个堆栈变量。
2、间接堆栈引用。编译器还依赖于堆栈变量的预定布局,并将生成通过不相关变量的地址访问变量的代码。
3、没有堆栈帧指针。作为优化,堆栈帧指针可以用作通用寄存器。这种优化让我们很难检测到可能的间接堆栈引用。
- 全局变量
程序中的所有函数都可以访问全局变量。由于这些变量与特定函数无关,因此它们通常放置在程序二进制文件的特殊部分中。与堆栈变量一样,攻击者也可以利用全局变量的特定顺序。
与堆栈一样,McSema将每个数据段视为一大块内存。堆栈变量和全局变量之间的一个主要区别是McSema知道全局变量从哪里开始,因为它们直接从多个位置引用。不幸的是,没有足够的信息来改变全局变量布局。McSema还需要知道每个变量的结束位置,这更难。目前,我们依靠DWARF调试信息来识别全局变量大小,但期待可以实现在没有DWARF信息的二进制文件上工作的方法。
让我们回顾一下我们做了什么。这真的很神奇!我们首先构建了一个使用异常和全局变量的简单C++程序。然后我们将程序翻译成LLVM位码,识别堆栈和全局变量,并保留基于异常的控制流。然后,我们使用多编译器对其进行了转换,并创建了一个新的、多样化的二进制文件,其功能与原始程序相同。
虽然这只是一个小例子,但这种方法可以扩展到更大的应用程序,并提供一种快速创建多样化程序的方法,无论是从源代码开始还是从以前的程序二进制文件开始。
- 结论
我们正在积极努力改进McSema中的堆栈和全局变量恢复。这些更高级别的语义不仅会创造更多的多样化和转换机会,而且它们还将允许更小、更精简的位码、更快的重新编译二进制文件和更彻底的分析。
我们相信CFAR和类似技术有着光明的未来:每台机器的可用内核数量不断增加,对安全计算的需求也在不断增加。许多软件包无法利用这些核心来提高性能,因此使用备用核心来确保安全是很自然的。McSema、多编译器和其他CFAR技术展示了我们如何将这些额外的内核用于服务以提供更强大的安全保证。
二、Galois公司的文章:使用自动化软件多样性保护应用程序
在DARPA的CFAR计划中,Galois的“RADSS”团队正在开发新的方法来减少针对传统C/C++系统的内存损坏攻击,而无需查找和修复每个单独的错误。CFAR是关于“网络容错攻击恢复”的,我们的一般方法是:
•给定一些要防御的应用程序,生成该应用程序的多个变体,使得所有变体在给定良性输入时表现相同,但在给定恶意输入时表现不同。
•在统一输入/输出的多变体执行环境(MVEE)中同时运行一组多个变体,并可以监控该集合,检测变体何时出现行为分歧,然后做出反应并恢复。
我们的重点是开发可以生成多种变体的工具,这些变体可以最大限度地检测攻击,同时保持在适合实际系统的性能范围内。我们的防御措施在CFAR的红队演习中被证明是有效和实用的,我们可以保护Apache Web服务器和其他现实世界的应用程序免受常见形式的攻击。在这篇文章中,我们概述了我们的方法以及这些防御措施最有效的地方。后续文章将介绍我们的防御如何抵御特定类型的攻击,以及组合变体生成技术可能无意中否定预期的安全保护的一些令人惊讶的方式。
- 变体生成策略
我们专注于表面内存损坏漏洞(例如缓冲区溢出)的方法,这些漏洞在许多用C/C++等不安全语言编写的过时系统中很常见。我们的策略是让变体在应用程序中保留明确定义的行为,但在未定义行为的影响中引入多样性(例如越界访问)。
我们的团队包括开发多编译器的UC Irvine和Immunant。他们在clang/LLVM编译器框架上构建了多编译器,通过添加许多转换,在生成的二进制文件中引入变体(称为软件多样性)。也就是说,从一个输入程序中,多编译器可以生成许多不同的输出二进制文件,这些二进制文件在正常情况下表现出相同的行为,但在受到攻击时表现出不同的行为。这是可能的,因为程序员编写的代码并未完全指定生成的二进制表示的所有方面,但低级漏洞对这些属性非常敏感。多编译器的转换范围包括从全局、堆栈、函数和vtables的内存布局的随机化,到生成的变体如何在内存中存储数据等更复杂的变化。
虽然多编译器通过编译源代码直接生成程序的变体,但我们也希望可以保护仅以二进制形式可用的系统(子系统/库)。我们的团队还包括Trail of Bits,他们让McSema来解决这个问题。McSema可以将二进制文件提升为LLVM位码,多编译器可以使其多样化,从而使我们能够生成应用程序的变体,即使它们仅以二进制形式可用。
(待续。来源:综合外网及外媒等。本文参考内容均来源于网络,仅供读者了解和掌握相关情况参考,不用于任何商业用途。侵删)
热门跟贴