一个基于自定义中间语言("FuzzIL")的(基于覆盖率的)动态语言解释器引导式模糊测试器,该中间语言可以被变异并翻译成JavaScript。
使用此模糊测试器的基本步骤如下:
swift build [-c release]。swift run [-c release] FuzzilliCli --profile=<profile> [other cli options] /path/to/jsshell。另请参阅 swift run FuzzilliCli --help。在Docker和Google Compute Engine中构建和运行Fuzzilli及受支持的JavaScript引擎也是支持的。
查看 main.swift 以了解Fuzzilli库的使用示例,并尝试各种配置选项。接下来,查看 Fuzzer.swift 以了解高级的模糊测试逻辑。然后可以深入研究任何看起来有趣的部分。
非常欢迎对此项目的补丁、添加和其他贡献!不过,请先快速查看 贡献者须知。Fuzzilli 大致遵循 Google 的 Swift 代码风格指南。
如果您通过此项目发现了任何漏洞,能通过 [email protected] 发送简短说明(可能包括 CVE 编号)或开启一个拉取请求,以便将其包含在漏洞展示部分,将不胜感激。除此之外,您当然可以保留任何漏洞赏金、CVE 积分等。
在模糊测试核心解释器漏洞(例如 JIT 编译器中的漏洞)时,生成程序的语义正确性成为一个关注点。这与大多数其他情况(例如模糊测试运行时API)不同,在后一种情况下,可以通过将生成的代码包裹在 try-catch 构造中来轻松处理语义正确性问题。有几种不同的方法可以实现可接受的语义正确样本比例,其中一种是基于变异的策略,即语料库中的所有样本也是语义有效的。在这种情况下,每次变异只有很小的机会将有效样本变成无效样本。
为了实现基于变异的JavaScript模糊测试器,需要定义对JavaScript代码的变异。不是对程序的AST或其他语法元素进行变异,而是定义一种自定义中间语言(IL),在该语言上可以更直接地进行程序控制流和数据流的变异。然后,该中间语言被翻译成JavaScript以执行。中间语言大致如下所示:
v0 <− LoadInteger '0'
v1 <− LoadInteger '10'
v2 <− LoadInteger '1'
v3 <− LoadInteger '0'
BeginFor v0, '<', v1, '+', v2 −> v4
v6 <− BinaryOperation v3, '+', v4
Reassign v3, v6
EndFor
v7 <− LoadString 'Result: '
v8 <− BinaryOperation v7, '+', v3
v9 <− LoadGlobal 'console'
v10 <− CallMethod v9, 'log', [v8]
例如,可以轻松地翻译成以下JavaScript代码:
const v0 = 0;
const v1 = 10;
const v2 = 1;
let v3 = 0;
for (let v4 = v0; v4 < v1; v4 = v4 + v2) {
const v6 = v3 + v4;
v3 = v6;
}
const v7 = "Result: ";
const v8 = v7 + v3;
const v9 = console;
const v10 = v9.log(v8);
或者通过内联中间表达式,翻译成以下JavaScript代码:
let v3 = 0;
for (let v4 = 0; v4 < 10; v4++) {
v3 = v3 + v4;
}
console.log("Result: " + v3);
FuzzIL 具有以下特性:
Reassign 指令)重新赋值。然后可以对这些程序执行多种变异:
关于 Fuzzilli 工作原理的更详细讨论,请参见此处。
该模糊测试器使用 Swift 实现,部分功能(如覆盖率测量、套接字交互等)使用 C 实现。
一个模糊测试器实例(实现在 Fuzzer.swift 中)由以下核心组件组成:
此外,还有一些可选模块:
模糊测试器是事件驱动的,大多数不同类之间的交互通过事件发生。事件例如因崩溃、发现有趣程序、执行新程序、生成日志消息等而分发。事件完整列表请参见 Events.swift。事件机制有效地解耦了模糊测试器的各个组件,并简化了额外模块的实现。
可以使用 ProgramBuilder 实例构建 FuzzIL 程序。ProgramBuilder 提供了创建和追加新指令、从另一个程序追加指令、检索现有变量、查询当前位置的执行上下文(例如是否在循环内部)等方法。
Fuzzilli 使用一种称为 REPRL(读取-求值-打印-重置-循环)的自定义执行模式。为此,目标引擎被修改为通过管道和/或共享内存接受脚本输入,执行脚本,然后重置其内部状态并等待下一个脚本。这消除了进程创建的开销,并在很大程度上消除了引擎初始化的开销。
每个目标进程有一个 Fuzzer 实例。这使得程序能够同步执行,从而简化了各种算法(如连续变异和最小化)的实现。此外,它避免了实现线程安全的内部状态访问(例如语料库)的需要。每个模糊测试器实例都有自己的 DispatchQueue,在概念上对应一个线程。通常,与 Fuzzer 实例的每次交互都必须在该实例的调度队列上进行。这保证了线程安全,因为队列是串行的。更多详情请参见文档。
为了实现扩展,模糊测试器实例可以形成树形层次结构,在这种情况下,它们将新发现的有趣样本和崩溃报告给其父节点。反过来,父节点将其语料库与其子节点同步。树中节点之间的通信可以通过不同的方式进行,每种方式都实现为一个模块:
这种设计允许模糊测试器在单个机器上扩展到多个核心,以及扩展到多台机器。由于如果太多实例向一个父节点发送程序,该父节点可能会迅速过载,因此可以配置多级实例,例如一个根实例、16个连接到根实例的中间节点,以及256个连接到中间节点的“叶子”节点。关于分布式模糊测试的更多信息,请参见 Cloud/ 目录。
有关本模糊测试器的更多资源:
以下是一些借助 Fuzzilli 发现的漏洞列表。只有至少出现在受影响软件 Beta 版本中并具有安全影响的漏洞才应列入此列表。由于 Fuzzilli 通常用于开发期间的持续模糊测试,因此许多由它发现的问题未包含在此列表中,因为它们通常在易受攻击的代码达到 Beta 版本之前就被发现。然而,最近由 Fuzzilli 在 V8 中发现的所有问题列表可以在此处找到。
特别感谢所有使用 Fuzzilli 并报告其所发现漏洞的用户!
这不是 Google 官方支持的产品。