探索软件中的“怪异机器”:漏洞利用与程序分析新视角
为什么“怪异机器”至关重要
攻击者寻找“怪异机器”来击败现代漏洞利用缓解措施。怪异机器是部分图灵完备的代码片段,它们固有地存在于函数和函数组周围的“松散契约”中。松散契约是指一段代码不仅实现了预期程序,而且其实现方式导致程序经历了比应有更多的状态变化(即程序状态的前提条件集比必要的大)。我们期望“紧密契约”以获得更好的安全性,即程序仅在确切预期前提条件下改变状态,不会出现“怪异”或非预期的状态变化。
怪异机器是一段代码片段,会以程序员未预期的方式处理有效或无效输入。
不幸的是,松散契约存在于大多数软件中,是诸如链表遍历、文件解析、小型单一用途函数以及任何其他从复杂系统中出现的功能的副产品。现代攻击者利用这些非预期计算,构建绕过漏洞利用缓解措施和安全检查的怪异机器。让我们看一个怪异机器的例子。
|
|
函数 ListItem::TrySetItem
看似有以下前提条件:
- 必须传入
this
和item
,两者都是指针 this
和item
必须是已分配和构造的ListItem
实例
然而,一旦生成机器码,实际前提条件是:
this
参数必须是指向至少8字节已分配内存的指针- 必须传入第二个参数(
item
),但它可以是任何类型
这是一个松散契约的例子,这是我们编写代码方式固有的。攻击者如果覆盖了 m_next
指针,可以利用此函数检查任意地址的内存是否设置:如果是,攻击者可能泄露内存;如果不是,攻击者可能设置内存。
漏洞用于改变程序执行或数据状态。此后的执行要么是怪异状态,要么是在非预期数据上操作的状态。
收紧契约
一种松散契约是“执行”契约,即程序中有效间接分支的可能地址集。
1995年的 Windows NT 是松散执行契约的一个例子,所有标记为“读取”的内存也隐含“执行”。这包括程序中的所有数据——不仅仅是代码。2003年,微软在 Windows XP SP2 中引入非可执行数据(NX,DEP)时收紧了执行契约。微软在2006年 Windows Vista 中引入地址空间布局随机化(ASLR)时进一步改进了契约,该技术随机化可执行代码的位置。2016年,随着 Windows 8.1/10 引入了控制流防护(CFG),验证间接分支的前向边指向一组批准的函数。
在下图中,明显看出剩余的有效间接目标很少。这种紧密的“执行”契约使得漏洞利用更加困难,对怪异机器的需求更大,显著增加了怪异机器的价值。如果我们能进一步收紧程序契约,将使怪异机器更难识别。
执行契约定义了程序的可执行区域(黄色)。随着契约收紧,这些区域多年来已减少。
“怪异”是什么样子
识别怪异机器是一个难题。当我们甚至不知道契约是什么时,如何识别松散契约?自动识别这些怪异机器将使我们能够正确分类漏洞是否实际上可利用,以及在没有怪异机器的情况下是否不可利用。
一种以编程方式描述怪异机器的方法是通过霍尔三元组。霍尔三元组描述了一段代码的执行如何改变计算状态:进入新状态所需的前提条件以及描述如何离开状态的后条件。当我们识别怪异机器时,可以通过移除它们或约束前提条件为状态期望的确切内容来自动收紧此类契约。这将使我们更接近创建可证明安全的程序。
回顾我们的例子,我们可以添加 dynamic_cast
来强制执行契约前提条件。如果我们分析代码片段作为霍尔三元组,我们注意到函数执行的前提条件是松散的,因此任何地址都可以传递给函数。此外,后条件不存在,因此一旦执行,函数将设置或返回内存,无论程序状态如何。
|
|
dynamic_cast
是运行时防护,检查以验证函数是否在预期指针上操作。这个新函数在利用中显然不如以前有用。
具有不精确定义前提条件的霍尔三元组允许发生“怪异”状态变化。通过改进输入检查来收紧这些前提条件将使这些状态无法实现。
那么我们如何找到它们?
在解决方案的道路上存在许多难题。第一个是规模。我们不关心简单的测试用例,我们关心的是今天部署给数十亿人的真实代码:浏览器、操作系统、邮件客户端、消息应用程序。自动识别这些平台上的怪异机器是一个重大挑战。
给定一组可能的执行路径及其对象创建和访问模式,我们必须识别具有特定和可控副作用的程序切片。这些切片本身必须是图灵完备的。这些“图灵 thunk”的行为可能在其正常放置位置之外或不同数据状态下有所不同。为了扩展我们的分析,我们可以将问题分解为子组件。
从识别图灵 thunk 开始,分析它们的副作用,并确定它们的可达性。我们可以使用数据流分析和形状分析来识别这些“图灵 thunk”并测量它们的副作用。将测量这些已识别怪异机器的副作用,以确定这些候选怪异机器如何组合。对全局状态的改变可能改变后续怪异机器的执行。数据流提供基于受控输入可转换的路径。形状分析有助于重建堆对象、布局以及对象之间的交互。这有助于确定生成执行路径到怪异机器所需的输入约束,以及执行怪异机器之前和之后的堆状态。
一旦识别出候选,可以根据特定功能和副作用进行优先级排序。我们可以使用符号和具体执行来验证这些候选,并使用机器学习按行为、执行约束和副作用对候选进行分组,以便后期查询更容易。
漏洞利用的未来
最终,怪异机器是漏洞利用的基本工具。随着程序变得更加复杂和缓解措施的堆积,怪异机器的重要性只会增加。在现实世界程序中找到这些图灵片段并枚举它们的属性将有助于下一代漏洞利用和安全。
一旦我们能够自动识别怪异机器,我们将有能力移除这些怪异状态,并确定程序的可利用程度。我们可能还能够证明特定漏洞不可利用。
解决方案的一部分是改进术语,需要成熟。解决方案的另一部分是进一步研究问题空间。虽然对该主题有兴趣,但我希望 DARPA 未来投资这一领域。
识别和分类怪异机器的工具和系统尚不存在。我们还有很多工作要做,但构建块已经存在。有了它们,我们将更接近解决明天的问题。
如果您想了解更多关于这一研究领域的信息,我建议您从这些出版物开始:
- 怪异机器、可利用性和可证明的不可利用性
- 漏洞利用编程:从缓冲区溢出到“怪异机器”和计算理论
- 利用作为代码重用:论形式化的必要性
- 证明携带代码中的怪异机器
如果您喜欢这篇文章,请分享: Twitter LinkedIn GitHub Mastodon Hacker News
页面内容 为什么怪异机器至关重要 收紧契约 “怪异”是什么样子 那么我们如何找到它们? 漏洞利用的未来 最近帖子 构建安全消息传递很难:对 Bitchat 安全辩论的 nuanced 看法 使用 Deptective 调查您的依赖项 系好安全带,Buttercup,AIxCC 的评分回合正在进行中! 使您的智能合约超越私钥风险 Go 解析器中意外的安全隐患 © 2025 Trail of Bits. 使用 Hugo 和 Mainroad 主题生成。