IPC快照模糊测试——攻防实战
进程分离仍然是Firefox安全模型中最重要的部分之一,保护我们的IPC(进程间通信)接口对于保持不同进程间的权限分离至关重要。今天,我们将更详细地了解我们用于在这些接口中发现漏洞的最新工具——快照模糊测试。
快照模糊测试
模糊测试IPC层的一个挑战是,隔离要测试的接口并不容易做到。相反,需要运行整个Firefox实例来有效模糊测试这些接口。然而,运行Firefox实例进行模糊测试带来了另一系列缺点:首先,除了重新启动整个浏览器外,我们无法轻松将系统重置回已知良好状态。这会导致可重现性问题,并破坏覆盖引导模糊测试所需的确定性。其次,父进程中的许多错误仍然通过崩溃来处理,再次迫使浏览器进行完整且耗时的重启。这两种情况本质上都是性能问题——重新启动浏览器太慢,无法实现高效和富有成效的模糊测试。
这就是快照模糊测试发挥作用的地方——它允许我们在"准备就绪"执行模糊测试时拍摄快照,并在每次模糊测试迭代后几乎无成本地重置到该快照点。即使我们在父进程中发现通常会迫使我们重新启动浏览器的错误,这种快照技术仍然有效。
技术实现
由于Firefox由需要保持同步的多个进程组成,我们决定使用Nyx,这是一个全虚拟机快照模糊测试工具。在此设置中,Firefox在客户操作系统(通常是Linux)中运行,拍摄的快照是整个客户机及其所有进程的快照。Nyx还与AFL++作为前端兼容,这是我们已经在其他模糊测试目标中使用的工具。
为了促进Firefox和Nyx之间的通信,我们使用了一个自定义代理,本质上是预加载到Firefox中的粘合代码。此代码处理与Nyx的低级通信,并负责向链接到Firefox的AFL++运行时提供跟踪缓冲区(用于覆盖测量),以及传递来自AFL++的模糊测试数据。在这种配置中,这两项任务都更加复杂,因为AFL++不是直接启动目标二进制文件并与之通信。该代理还向Firefox暴露了一个干净的接口,可用于在Firefox本身中实现实际的模糊测试器,而无需担心低级细节。
快照模糊测试技术栈概览。
在此接口之上,我们实现了多个IPC模糊测试目标,最简单的是IPC_SingleMessage,我们现在将更详细地查看它。
模糊测试单个IPC消息
修改传输中的单个IPC消息通常是IPC模糊测试的基本方法之一。如果目标消息类型本身很复杂(单个消息中包含大量数据,而不是由大量简单消息组成的复杂接口),则此方法特别有用。
为此,我们在父进程的目标线程中拦截消息,然后再将它们分派到最终调用IPC方法的生成IPC代码。大部分逻辑包含在IPCFuzzController::replaceIPCMessage中,它主要执行以下两件事之一:
- 如果消息类型与我们配置的目标消息不匹配,我们可以选择将其转储到文件中(这对于为不同类型的单消息模糊测试创建种子很有用),否则我们传递消息。
- 如果消息匹配我们的目标规范,则拍摄快照,用我们的模糊测试数据替换原始消息的有效负载,并返回要分派的新(模糊测试)消息。
一旦模糊测试的消息被分派(最常见的是到不同的线程),我们就面临多线程快照模糊测试的一个重要挑战:同步。覆盖引导模糊测试通常假设我们知道模糊测试数据何时被处理。根据模糊测试目标,很难判断我们何时"完成",但在我们的情况下,因为我们已经在运行实际IPC方法的目标线程上。因此,除非该方法再次执行异步分派,否则我们可以等待分派返回,我们在DispatchMessage()的末尾这样做,在那里我们回调IPCFuzzController以释放(恢复到快照)。
通过将此目标与CI测试¹结合,我们现在能够发现实现缺陷,例如涉及ShowEvent消息的可访问性代码中的漏洞。此消息包含一个序列化的AccessibleData数组,使此消息类型成为单消息模糊测试的良好目标。
测量快照模糊测试中的代码覆盖率
代码覆盖率可能是长期模糊测试活动中最重要的指标,因为它突出了模糊测试的潜在缺点。虽然对于大多数模糊测试来说,生成代码覆盖率相当简单,但在快照模糊测试中这样做就不那么简单了。由gcov等工具提供的传统源代码覆盖率在其他模糊测试中使用,但不容易部署,因为数据必须在每次迭代时从虚拟机中拉出,以便在快照恢复重置数据之前保存。这样做会使获取代码覆盖率的过程慢得不可行。
相反,我们决定在现有检测的基础上构建自己的代码覆盖率测量。为此,我们添加了一个新的AFL++检测类型,该类型检测所有基本块,然后在AFL++中创建第二个永久跟踪缓冲区,累积常规跟踪缓冲区的覆盖率。最后,我们创建第三个称为pcmap的缓冲区,它将跟踪缓冲区中的每个条目映射到二进制文件中的一个地址,该地址稍后可以使用调试信息解析为源代码位置。由于此信息包含在AFL++运行时中,我们需要在我们的自定义Nyx代理中获取它并将其写出到主机。同样适用于表示Firefox模块加载地址的模块信息。通过结合这三个信息源,我们可以将Nyx模糊测试的进度映射到实际源代码上。我们还构建了额外的工具,使用来自gcov构建²的信息将此基本块覆盖率转换为基于行的覆盖率。因此,我们可以生成像代码覆盖百分比这样的指标,以评估基于快照的模糊测试的整体有效性。
结论
虽然快照模糊测试是一项相当复杂的技术,有许多移动部件,但它使我们能够有效地压力测试浏览器的代码区域,这些区域否则将超出传统模糊测试技术的能力范围,但对于提供足够的安全保证至关重要。我们很高兴地报告,这种新的模糊测试技术正在成为常态,并且现在是我们安全测试策略的重要组成部分。我们要感谢Nyx和AFL++的作者使这项技术可用,并希望我们的共同努力将帮助其他人将快照模糊测试适应他们的项目。
¹ Firefox运行许多持续集成测试,以确保浏览器的每个功能都自动测试。 ² 不幸的是,gcov和调试信息在某些情况下存在偏差,因此结果还不是100%准确的映射,并且不能与其他gcov数据无缝合并。使用LLVM注释获取额外的基本块信息可能会改进这一点。