vlambda博客
学习文章列表

一种针对JavaScript引擎JIT编译器的模糊测试方法


摘 要


JavaScript引擎的安全性是浏览器安全的重要一环,以往针对JavaScript引擎的模糊测试工具很难针对JIT编译器部分进行有效挖掘。对此提出了一种针对JIT编译器的模糊测试方法,使用基于中间语言的生成算法生成JIT种子文件,在保持该种子文件中间语言结构的情况下进行变异。相比于其他模糊测试工具,本方法的测试样本有更高的模糊测试JIT编译器的成功率,并能达到更高的JIT编译器的覆盖率,从而表明该方法针对JIT编译器的模糊测试具有更好的性能优越性。

内容目录:

1 研究现状

1.1 CodeAlchemist

1.2 Fuzzilli

1.3 DIE

1.4 分析

2 设计思路

2.1 生成JIT种子文件

2.2 检测

2.3 变异

3 实现

3.1 中间语言

3.2 上下文容器

3.3 变量

3.4 程序执行

4 测试与分析

4.1 实验指标

5 结 语

 Web浏览器安全是用户在网络环境中至关重要的一环,而JavaScript引擎是浏览器中的重要组件,攻击者可以通过钓鱼网页轻易地让用户触发JavaScript引擎漏洞。JavaScript引擎又是内存危险的,触发漏洞后攻击者可以构造特殊的原语来实现内存的任意读写,从而控制被攻击者的设备。JavaScript引擎漏洞在近几年频繁出现,伴随的利用脚本都仅仅需要远程浏览器访问,范围覆盖Windows、MacOS、iOS、Android各种操作系统,故JavaScript引擎的安全问题已经是安全研究的重点对象。
近几年来,JavaScript引擎漏洞中JIT编译器的漏洞占据了越来越大的比重。JIT编译器,全称(Just-In-Time)是一种程序语言提高运行效率的方法。如图1所示,JavaScript引擎解释执行JavaScript的流程为:先通过词法和语法分析将JavaScript脚本转化为AST树,然后编译为中间语言字节码,接着进行逐步解释执行,当执行的过程出现多次循环或者多次函数调用时,JIT编译器会将该部分字节码重新编译,通过编译优化提升运行效率,最终转化为符合操作系统的机器码并执行。

图1 解释执行JavaScript流程
JIT优化阶段,JIT会将优化拆分成为多个步骤(如常数折叠、循环不变量提升等),这些步骤即为每一个具体的JIT优化阶段。JIT优化时会将需要优化的代码进行分析,当其满足优化阶段的条件时,进行优化。
JIT编译器漏洞,主要包含的是JIT优化相关的漏洞,优化漏洞通常在JIT编译优化过程中利用错误预测或绕过检查而达到利用。其中包含绕过边界检查,如CVE-2015-0817、CVE-2017-2547和 CVE-2018-0769;绕过类型检查,如CVE-2017-11802、CVE-2018-17463和CVE-2018-4233;以及各类其他类型漏洞。
模糊测试是一种自动化漏洞挖掘技术。尤其在针对浏览器引擎这类复杂的系统时,模糊测试相比代码审计等其他方式有着更高的挖掘漏洞效率。其核心思想是将随机生成的输入重复提供给应用程序,然后在处理输入直至程序退出期间,监视程序是否出现错误情况。模糊测试主要分为两类,基于生成的方法和基于变异的方法。分述如下:
基于生成的模糊测试方法,每个输入文件都是从头开始生成的,通常遵循一组预定义的规则。该规则将是上下文无关并会限定所有输入的集合。在生成的过程中,通过随机选择实现随机生成。
基于变异的模糊测试方法,是从一组已知良好的种子文件开始,然后以随机方式对它们进行变异。可能的变异包含比特和字节翻转、递增和递减整数值、插入预定义的特殊整数和字符串值等。
在模糊测试程序运行后便会不断生成测试样本,让目标程序执行,获取执行结果并统计。衡量模糊测试对目标程序漏洞挖掘的进度,通常使用的是覆盖率指标。覆盖率是通过桩来计算的,桩存在于目标程序的分支跳转和函数调用处,通常都是在编译目标程序时由编译器完成插桩工作。每次执行目标程序后,模糊测试工具可以获得执行过程中抵达的桩信息,计算抵达过的桩数量相对于总数的占比,即为覆盖率指标。
为了能尽快提高覆盖率,提出了基于覆盖率为导向的模糊测试方法,当样本抵达了新的桩,则将其确定为“有趣”的样本保留,在未来继续对其进行变异,如此往复,逐渐遍历系统的所有空间。

01

研究现状

在现今针对JavaScript引擎的模糊测试工具中,有基于规则生成的,也有基于变异生成的,本节会介绍当今主流的几款针对JavaScript引擎的模糊测试工具,并分析其使用的模糊测试技术。

1.1 CodeAlchemist

这是一款基于语料库的生成模糊测试工具,从JavaScript种子文件中分解出代码单元片段,对其进行变量重命名、数据流分析、类型分析后并放入代码块池中,然后对代码块池中的片段进行随机选择,组合生成JavaScript测试样本。CodeAlchemist在组合代码片段时,会考虑上下文约束,从符合变量类型和数量的代码片段中随机选择。该方法成功提高了模糊测试样本的成功率。

1.2 Fuzzilli

这是一款基于中间语言的模糊测试工具,Fuzzilli定义了一种新的中间语言FuzzIL并在其上进行生成和变异,在组合了一系列中间语言后,统一将其提升为JavaScript测试样本。提升过程时基于上下文和类型系统,测试样本有着较高的JavaScript语义正确性,并且变异过程是基于覆盖率导向的,可以达到较高的覆盖率。

1.3 DIE

这是一款基于类型语法树的变异模糊测试工具,将JavaScript种子文件转为语法树,然后通过类型分析,产生包含类型标识的语法树(Typed AST)。DIE基于类型语法树并保留种子文件结构和类型特征进行变异,优质的样本对于DIE非常重要,故其选择了大量以往的JavaScript崩溃样本。

1.4 分析

对于现今针对JavaScript引擎的模糊测试工具而言,测试样本的成功率和覆盖率是重要的衡量指标。对于JavaScript引擎中的JIT编译器,测试样本成功率和覆盖率同样是衡量模糊测试性能的重要指标。其中测试样本成功率指成功进行JIT优化并执行优化代码的样本的比例,而覆盖率指JIT编译器部分的覆盖率。
当今针对JavaScript引擎的模糊测试工具对JIT引擎的测试样本成功率不高。原因为:(1)JIT引擎需要特殊的条件进行触发,并不是所有测试样本都会触发JIT优化;(2)JIT代码会出现优化退出的情况,而以一个会产生优化退出的样本作为种子文件时,会产生许多不成功的测试样本;(3)生成过长的样本会影响JIT的运行时间而导致超时。
当今针对JavaScript引擎的模糊测试工具对JIT引擎的覆盖率也不高。原因为:(1)需要拥有优质的JIT种子文件作为变异样本;(2)在模糊测试JIT优化阶段时较为盲目,无法针对某一JIT优化阶段进行专一的探索。

02

设计思路

该节会介绍本模糊测试引擎的设计思路,包含生成JIT种子文件、检测和变异三个部分。

2.1 生成JIT种子文件

从触发JIT优化条件而言,需要一个循环被多次执行,这样在循环内部的代码将会被JIT优化。故设计了一个JIT种子文件的模板,包含:(1)一个将要被优化的opt函数内部;(2)触发JIT优化的循环;(3)外部不被优化的代码。如图2所示,在修改了JavaScript引擎的运行参数后,可以将多次调用的数量减少为10~1000次。
在生成JIT种子文件时,采用基于中间语言的生成方式。 相比基于语法或基本块的生成方式,中间语言可以构造更为泛型和复杂的句法结构。 另外在从中间语言提升为JavaScript的过程中会考虑上下文约束,每一个变量不仅保存类型信息,还有方法、属性、原型链信息,相比基于类型语法树的方法能够生成语义正确性更高的样本。 如图3显示了如果只通过类型语法树进行生成可能导致的语义错误,由于未考虑到原型链,会认为变量a作为数组类型仍拥有splice方法。 图2 种子文件生成结构

一种针对JavaScript引擎JIT编译器的模糊测试方法

图3 单一的类型系统可能发生的语义错误
2.2 检测

在检测上,除了崩溃捕获、运行时长和覆盖率信息,还需要检测JIT优化是否成功执行。JIT编译后会在代码中插入一些检查来确保JIT优化的正确执行,而当检查不被通过时,JIT优化将会被退出,返回到原本的字节码进行逐步解释执行。在这种情况下,该测试样本并没有对JIT编译器进行有效的模糊测试。
JIT种子文件在生成后需要被执行一次,统计其运行时长、覆盖率和JIT执行情况,并以此决定其是否被设置优先级放入变异队列中。如果生成的JIT种子文件没有触发JIT优化或退出JIT优化,则认为对该种子文件的变异无法有效地对JIT进行模糊测试,将会抛弃该种子文件。当一个JIT种子文件抵达了新的桩,成功执行了JIT优化,并且有较短的运行时长后,该种子文件将会被设置一个较高的优先级放入变异队列中。

2.3 变异

变异会选择一个JIT种子文件,在不改变其中间语言结构的情况下,重新生成中间语言中可替换的变量,产生新的测试样本。使用该方法的原因是触发JIT优化每个阶段的条件和每一条JIT优化的JavaScript语句都有较大的关系。如果如Fuzzilli在每次变异进行插入、合并等细粒度较大的变异操作,将会大幅修改测试样本结构,从而改变已触发的JIT优化阶段,导致盲目地对JIT优化阶段进行模糊测试。而本方法保持了中间语言结构,并在此之上进行的变异能大概率确保触发的测试样本的JIT优化阶段和JIT种子文件的JIT优化阶段相同,从而能更深入地探索JIT优化阶段,以达到更高的覆盖率。
种子文件的能量分配规则类似主流的模糊测试工具。但对于优化函数内部和外部的能量分配和变异方式却是不同的。每次对一个种子文件进行变异会执行数次基于中间语言的变异操作,每次操作在优化函数内部的概率会大于在外部的概率。在JIT优化函数内部会采用保留中间语言结构的变异方式,而在优化函数外部则会细粒度大的变异方式。

03

实 现

本节会分别叙述本模糊测试工具实现上较为核心的几个部分。

3.1 中间语言

中间语言是一种抽象的操作,本方法定义了如LoadProperty,StoreElement,CallMethod,BeginIf等中间语言,中间语言需要尽可能涵盖JavaScript的各种语言特征,如变量声明、赋值语句、一元操作符、二元操作符、函数调用、方法调用、创建对象、控制流等,如图4所示。在生成中间语言后,对其逐条分析其上下文环境,从已有的上下文容器中获取合适的变量,然后再分析该条中间语言生成的返回值变量,将其存入上下文容器中,最终提升为JavaScript语句。

一种针对JavaScript引擎JIT编译器的模糊测试方法

图4 中间语言描述
3.2 上下文容器

系统需要知道每一条中间语言执行后,上下文变量的情况,来确保语义的正确性。通过维持一个容器,在分析中间语言的过程中实时地将上下文变量存储在容器中。容器中存储着JavaScript内置的构造器对象,也会将全局变量和局部变量存储其中。在逐条分析中间语言时,当遇到if-else、for循环、函数声明等会产生新作用域的中间语言,会生成一个新的域环境,在新作用域中生成的变量会被存储在新域环境中。当退出该作用域时,会将该域环境以及其中的变量全部删除。容器同时为系统提供获取当前上下文环境下各种类型的变量接口。

3.3 变量

每一个变量都继承于Variable类,该类包含了变量名、类型、方法、属性、原型链和函数签名。在JavaScript中的内置类型有许多,本方法将其分类为基础的Undefined、Int、Float、String、Object、Boolean、Function、Constructor。另外针对Object对象再根据不同的构造器划分,包含Symbol、Map、Set、ArrayBuffer等。每一种类型都存储了属性名和方法名以及原型链的上一个对象指针。对于内置对象的方法或者自定义的函数,会存储定义方法所需参数和返回值的类型作为函数签名,以便在调用方法时准确地从上下文容器中取出可用的变量。
值得注意的是,在调用方法的过程中,需要考虑原型链的规则,优先寻找当前对象的方法是否被定义,然后是构造器的方法是否被定义,再寻找内置的方法。如果没有则去寻找原型链上一个对象的构造器方法直至到达原型链头节点。如图5所示,定义了JavaScript中Array的内置方法,在该对象中,键名对应着方法名,值对应着参数列表的类型,数组最后一项为返回值类型。

一种针对JavaScript引擎JIT编译器的模糊测试方法

图5 系统对JavaScript内置方法的描述

3.4 程序执行

生成测试样本算法如图6所示。

一种针对JavaScript引擎JIT编译器的模糊测试方法

图6 生成测试样本算法      
模糊测试对于运行效率有着很高的要求,所以系统应尽可能提高运行效率。 系统的限速瓶颈在于JavaScript代码的执行时长,故参考了AFL的思路,使用了Fork Server来节约进程的创建开销。 系统架构如图7所示,模糊测试系统在选择了一个种子文件后会进行变异并生成JavaScript测试样本,而在另一处,Fork Server在解释执行JavaScript代码前等待信号,接收到信号后fork进程执行样本,获得结果并统计执行结果(崩溃、运行错误、超时、运行成功)。 图6 生成测试样本算法       

一种针对JavaScript引擎JIT编译器的模糊测试方法

图7 系统运行模糊测试流程

04

测试与分析

本模糊测试工具JITFuzz使用Python3开发,实现生成种子文件和变异功能,Fork Server和执行结果统计使用C语言编写。

4.1 实验指标

采用成功率、超时率、JIT成功率、JIT覆盖率作为评价本模糊测试系统的主要指标,描述如下。
成功率:执行成功的测试样本数量/测试样本总数。
超时率:执行超时的测试样本数量/测试样本总数。
JIT成功率:执行成功并成功触发JIT优化的测试样本数量/测试样本总数。
JIT覆盖率:抵达的桩的数量/JIT编译器的桩的总数。

4.2 实验结果

一种针对JavaScript引擎JIT编译器的模糊测试方法
图8 测试样本成功率和超时率统计
如图8所示,JITFuzz和Fuzzilli的测试样本成功率和超时率较为接近,优于CodeAlchemist和DIE的测试样本成功率和超时率。说明本方法采用了基于中间语言的生成和变异算法,并考虑了上下文和变量特性,有效地提升了测试样本的JavaScript语义正确性,能够生成正确性更高的测试样本。
考虑在所有测试样本中,成功触发JIT优化的比例,统计结果如图9所示,JITFuzz相比CodeAlchemist、DIE、Fuzzilli都有更高的JIT成功率。这说明本方法采用生成JIT种子文件和基于中间语言结构的变异方法所产生的样本,有更高的成功执行JIT优化的概率。
一种针对JavaScript引擎JIT编译器的模糊测试方法
图9 测试样本JIT成功率统计
此外,JITFuzz和Fuzzilli分析比较了模糊测试的JIT覆盖率情况。通过对JavaScript引擎中JIT编译器部分的插桩,统计覆盖率情况如图10所示。
一种针对JavaScript引擎JIT编译器的模糊测试方法
图10 JITFuzz和Fuzzilli的JIT覆盖率统计
相比于Fuzzilli,JITFuzz抵达的JIT覆盖率是Fuzzilli的1.75倍,并且在相同的样本数量下,JITFuzz能达到更高的覆盖率,说明单位样本触发覆盖率的数量更高,体现了本方法所采用的变异方法在针对JIT编译器进行模糊测试时有更好的效果。

05

结 语

本文提出了一种针对JavaScript引擎JIT编译器进行模糊测试的方法。该方法借鉴了前人的思路,采用了基于覆盖率和中间语言的技术,为能够顺利对JavaScript引擎进行模糊测试打下了良好的基础。
本文在此之上针对JIT编译器提出了新颖的模糊测试方法,首先利用触发JIT优化的模板生成JIT种子文件,并针对合适的种子文件进行保持中间语言结构的变异。这样可以使得测试样本有更高的JIT优化概率和对JIT优化阶段更为专注的探索,有利于覆盖率的提升。
实验结果表明,相比于现今较为流行的模糊测试工具,基于本文所实现的模糊测试工具JITFuzz有更高的JIT成功率和JIT覆盖率,表明本方法针对JIT编译器的模糊测试具有更好的性能优越性,为进一步的漏洞挖掘提供了良好的基础。
引用本文:王越,孙亮,王轶骏,等. 一种针对JavaScript引擎JIT编译器的模糊测试方法[J].通信技术,2021,54(01):175-180.


作者简介 >>>

王 越,硕士,主要研究方向为网络空间安全;

孙 亮,硕士,主要研究方向为网络安全、电子物证鉴定。

王轶骏,硕士,讲师,主要研究方向为网络攻防及系统安全;

薛 质,博士,教授,主要研究方向为计算机通信网及信息安全。

基金项目:国家重点研发计划项目“网络空间安全”重点专项(No. 2017YFB0803203)

选自《通信技术》2021年第1期(为便于排版,已省去原文参考文献)

网络强国建设的思想库

安全产业发展的情报站

创新企业腾飞的动力源

投稿网址: