论文笔记:Fuzzing JavaScript Engines with Aspect-preserving Mutation

简介

模糊测试技术(Fuzzing)是现今最具有可实施性的Bug查找技术。它被广泛应用在真实世界中的复杂程序中。

模糊测试器(Fuzzer)主要分为两类,分别称为生成性的模糊测试器(Generative Fuzzer)和变异性的模糊测试器(Mutational Fuzzer):Generative Fuzzer利用相应编程语言的语料库(Corpus)中已经分拆好的代码块,通过语言的文法或预先定义的规则将它们装拼起来作为种子文件。Mutational Fuzzer通过更改已有的PoC或测试样例中的部分代码来产生新的种子文件。

但作者认为,这两种类型的方法都不能充分利用高质量的语料库。因为上述两种方法不但没有良好的保持一些作为PoC的固有属性,反而为了代码覆盖率的因素破坏了它们。

为了解决上述问题,本文的作者创新性地提出了“方位保持变异”(Aspect-preserving mutation)的方法来保持PoC的固有属性。该理论结合了Generative和Mutational的特性,对于其中的一大部分代码保留,一小部分代码通过Generative的方法进行生成。生成时,注意维持其中的固有属性。其中,固有属性包括两种:结构保持(Structure preservation)和类型保持(Type preservation)。为了达到这一目的,作者对已有的语料库文件进行建模,形成带类型的抽象语法树(Typed Abstract Syntax Tree)模型。在语法树的基础上施加相应的变异。

作者将该理论运用进了他们最先进的JavaScript Fuzzer,DIE。他们利用DIE对ChakraCore,JavaScriptCore和V8进行了Fuzz。结果产生了48个Bug,有12个分配了对应的CVE编号。

动机

当下的Fuzzer不能够充分的探索JavaScript引擎的深层次代码及其中存在的Bug,主要的原因如下:
1、搜索空间过大:当下的Fuzzer为了触发内存损坏(Memory Corruption)错误,一味的追求代码覆盖率。但对于现今的JS引擎,这种简单错误已经减少。更多的是深层次的逻辑错误。
2、不能充分的利用已有的语料库:能够触发JIT的代码都需要有一定的前置条件,并且这些前置条件有相当多的共同点。例如,执行次数较多的for循环会触发JIT。其它类型的Fuzzer没有很好的保持这些前置条件,从而降低了产生Crash的机会。

主要方法及工作流程

Structure-preserving mutation: 保持程序的主要结构,例如控制流。这样能够增加保留触发JIT的前置条件的可能性。
Type-preserving mutation: 通过对语料库文件建模,形成带类型的抽象语法树。每次对子树对应的代码段进行更改时,始终保持它们的返回类型或值类型基本一致,以减少出现语法或类型等错误的可能性。

Workflow:
前处理部分:首先对原始的种子文件送入插桩后的引擎进行动态类型分析,得到对应的类型分析。结合静态文法识别出的AST,产生对应的Typed-AST并送入语料库。
种子生成部分:变异引擎(Mutation Engine)从语料库中选取Typed-AST加以修改,形成变异的Type-AST,再由该AST反推回JS文件,送入Fuzzing平台。
执行/反馈部分:由分布式Fuzzing系统进行执行,产生覆盖率反馈和崩溃报告。其中覆盖率反馈闭环传递至语料库,崩溃报告发送给工程师。

评估结果

1、DIE可以在现实世界的JS引擎中找到新的Bug,并且还不少。
2、本篇论文提出的“方位保持”在找到Bug方面起到了关键作用。一是“方位”(即固有属性)确实能很好的触发Bug,二是DIE确实保持了语料库中相应程序片段的“方位”。
3、DIE相对其他类型的Fuzzer,能够较好地产生在语法和语义上正确的JS程序。
4、在输入空间(代码覆盖率)及Crash产生率上,DIE相对其他Fuzzer都有优势。

展望

1、种子优先级:因为种子的质量不同,在做种子的变异时,可以对种子实现优先级的排队。
2、规则优先级:可以根据测试需要,对程序中Generative的部分的产生规则也实现优先级的排队。
3、“方位”的标注:利用人工标注代码中“方位”的特性。
4、“方位”在其它语言的推广:“方位”的思想对于其他语言也是通用的,可以将这一思想拓展到其他语言上编写Fuzzer。

引用

[0] 论文链接:https://ieeexplore.ieee.org/document/9152648/

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注