博士论文:语言解释器中堆溢出的灰盒自动漏洞利用生成
2020年夏季,我完成了博士论文答辩。论文可在此处找到。
以下是一个超级简短的总结(在冗长版本之前;)):
2016年之前的漏洞利用生成主要专注于针对网络守护进程和文件解析器等中的基于栈的缓冲区溢出的单次、完全自动化利用。在我看来,此类系统的架构不太可能支持更复杂漏洞类别、不同目标软件以及存在缓解措施情况下的漏洞利用生成。受模糊测试在漏洞发现方面成功的启发,我希望将灰盒输入生成应用于漏洞利用生成。作为一个整体问题,漏洞利用生成对于这种方法来说过于复杂,因此我开始将漏洞利用生成分解为多个阶段,并为每个阶段实现灰盒解决方案。我设计这些阶段为可组合的,并使用模板语言来实现阶段间解决方案的通信。可组合的求解器解决方案和人类可写的模板语言为漏洞利用生成引擎提供了两个新的强大能力:1)“人在循环中”意味着我们可以自动化当前可自动化的部分,同时允许人类解决我们没有良好求解器的问题;2)如果解决方案是可组合的,并且可以为任何阶段生成模拟解决方案,那么我们可以无序地解决阶段。如果一个阶段的解决方案生成成本高昂,但我们可以模拟它并查看这样的解决方案是否对后续阶段有用,然后如果确实有用再回来解决它,那么这将带来效率提升。在实践中,我利用这一点假设特定的堆布局,检查是否可以从该点创建漏洞利用,并且只有在发现可利用时才回来尝试实现该堆布局。我相信,漏洞利用生成系统的未来将是这样的架构:模糊测试式的输入生成机制应用于漏洞利用生成中的粒度任务,并且这些问题的解决方案通过模板语言组合,允许在必要时由人类提供解决方案。符号执行将发挥有限但重要的作用,当需要精确推理时,其他过度近似的静态分析可能会用于缩小输入生成和符号执行引擎必须考虑的搜索问题。您可以在第1章找到我所作假设的详细描述,以及在我看来一些最有趣的未来工作的概述,在最后一章。
背景
我最初在2009年从事漏洞利用生成工作(硕士论文在此处),我开发的方法是使用具体符号执行构建SMT公式,表示导致程序崩溃的输入路径语义,然后将这些公式与表达成功漏洞利用的逻辑条件结合,并请求SMT求解器找出产生所需输出所需的输入。这在某些保护机制有限的情况下有效(例如物联网垃圾),但有许多限制阻止它成为现实世界自动漏洞利用生成(AEG)解决方案的合理路径。具体符号执行存在一直存在的问题,即将其扩展到像Web浏览器这样的东西是一个开放问题,但致命缺陷更概念化,值得解释,因为它是我所知的每个漏洞利用生成系统的核心缺陷,直到包括参与DARPA网络大挑战的所有系统。
这项早期工作(我的和其他人的)基本上将漏洞利用生成视为一个两阶段过程。首先,他们使用普通模糊测试或符号执行工具找到一条被认为可利用的路径。在这种情况下,“可利用”几乎总是转化为“栈上的返回地址被覆盖”,然后该输入被传递到第二阶段以将其转换为漏洞利用。第二阶段包括重写原始输入中损坏函数指针或返回地址的字节,并可能重写一些其他字节以提供将执行shell或类似内容的负载。这种重写通常通过查询SMT求解器来完成。
这种方法的概念缺陷是认为模糊测试发现的崩溃输入可以一步转换为功能正常的漏洞利用。实际上,来自模糊测试的崩溃输入通常只是产生漏洞利用旅程的起点,并且更常见的是,一旦错误被手动分类,导致该崩溃的初始输入大部分被丢弃。漏洞利用开发者随后将开始将其用作可能涉及多个阶段的更大漏洞利用的一部分,并利用该错误作为一个组件。
2016年左右的开放问题
从2009年到2016年,我从事其他工作,但在2016年,我决定重返学术界攻读博士学位,并重新拾起这个话题。回顾参与DARPA CGC [3]的系统以及先前的工作,有一些明显的有趣开放问题和机会:
- 我找到的所有系统都专注于完全自动化的漏洞利用生成,没有能力与人类协同进行漏洞利用生成。
- 我能找到的研究尚未涉及“利用原语”的概念,这在手动构建漏洞利用中是基础的,并且可能在任何自动化或半自动化方法中也是基础的。
- 所有系统将AEG视为 essentially 一个两步过程:1)找到触发错误的路径,2)一次性将该路径转换为漏洞利用,而不是一个多阶段“编程”问题 [4]。IMO,这是这些系统的主要限制,如上所述,也是它们无法扩展到更现实场景的原因。
- 没有人真正广泛利用灰盒输入生成方法(模糊测试),除了错误发现任务。
- 几乎所有系统仍然专注于基于栈的溢出。
- 没有人致力于在他们的漏洞利用生成系统中集成信息泄漏或处理ASLR。
- 没有人致力于语言解释器/浏览器。
- 没有人致力于内核。
很可能第5-8点是第1-4点的“原因”。一旦你决定开始处理第5-8点中的任何一点,必然地,你必须开始思考多阶段漏洞利用生成,获得人类协助,处理除基于栈的溢出之外的其他错误类别,并使用灰盒方法以避免符号执行中的可扩展性陷阱。
研究
考虑到以上内容(我没有涉及第6或第8点),我博士的主要目标是看看我是否能探索和验证一些我认为将推动最先进技术并形成未来AEG工具基础的想法。这些想法是:
- 真实的漏洞利用通常是相对复杂的程序,具有多个不同的阶段,其中必须解决不同的任务。就像“正常”程序合成一样,不同的任务可能需要不同的求解器。此外,通过分解问题,只要不同阶段的解决方案是可组合的,我们自然使其更容易解决。因此,我的第一个目标是将利用过程分解为不同的阶段,意图为每个阶段实现不同的求解器。将漏洞利用生成任务分解为多个不同阶段的一个有趣副作用是,它允许我集成“惰性”解决这些阶段的想法,并无序地解决它们。在实践中,这意味着如果早期阶段的求解器比后期阶段昂贵得多,那么我的系统允许人们“模拟”早期阶段的解决方案,并且只有在验证拥有它的解决方案实际上会导致漏洞利用时才回来真正解决它。
- 一旦漏洞利用生成被分解为多个阶段,我们必须决定如何解决每个阶段。符号执行推动了大多数漏洞利用生成引擎的核心,但它具有可怕的性能特征。另一方面,模糊测试已证明自己更具可扩展性,并适用于更广泛的程序集。为什么模糊测试以前没有作为漏洞利用生成的主要组件起飞?嗯,如果你将漏洞利用生成视为一个单一的、整体的问题,那么状态空间 simply 太大,并且没有合理的反馈机制来导航它。使用突变进行输入生成的灰盒方法真的需要某种梯度来扩展和评分机制来告诉它们它们做得如何。一旦我们将问题分解为阶段,为每个阶段设计反馈机制变得容易得多,并且每个阶段的状态空间规模也 drastically 减少。因此,我的第二个目标是为我的利用管道中的每个阶段设计纯粹灰盒、模糊测试启发的解决方案。我有意承诺完全灰盒以看看我能推动这个想法多远,尽管在现实中你可能会为一些外科手术任务集成一个SMT求解器。
- 我知道我不太可能在第一次尝试时就为每个管道阶段找到最佳求解器,甚至可能一个特定管道阶段的真实解决方案本身就是一个完整的博士研究。因此,重要的是使人们能够将特定自动化解决方案交换为人类提供的解决方案或替代求解器。我的第三个目标是然后找出一种方法,使多个求解器能够交互、可交换,并允许人类在循环中。为了实现这一点,我设计了一种模板方法,借此每个阶段和人类漏洞利用开发者可以编写和更新模板语言中的漏洞利用,后续阶段可以理解和处理。我在此处写了这在实践中的样子。
除了这些目标,我必须决定一个错误类别和目标软件类型来处理。基于栈的溢出已经被做烂了,所以我决定选择没有受到关注的基于堆的溢出。我还决定针对语言解释器(实践中是PHP和Python),因为它们无处不在,并且直到那时没有漏洞利用生成研究专注于它们。
最终,这项研究非常有趣,并希望对其他人有用。我认为将利用分解为多个阶段、尽可能使用灰盒解决方案以及使用模板驱动的利用来集成各种求解器或可选地人类的想