PolyFuzz:针对多语言软件的灰盒模糊测试|技术进展
2023-5-22 09:39:44 Author: mp.weixin.qq.com(查看原文) 阅读量:3 收藏

基本信息

原文名称    POLYFUZZ: Holistic Greybox Fuzzing of Multi-Language Systems

原文作者   Wen Li;Jinyang Ruan;Guangbei Yi;Long Cheng;Xiapu Luo;Haipeng Cai

原文链接   https://www.usenix.org/system/files/

sec23summer_411-li_wen-prepub.pdf

发表期刊   32ND USENIX Security Symposium,2023

开源代码   https://figshare.com/s/8ba4650e3248

197fd756

一、 引言

搭建软件系统时使用多种编程语言可以结合不同语言的优势,构建出更加高效和性能更优的软件。随着多语言搭建的软件数量的增长,多语言代码引发的软件系统安全漏洞也在增多。目前针对软件系统安全漏洞的主要检测方法包括静态分析和动态分析,但是静态分析对于语言的高度依赖导致其存在扩展性较差,难以支持多语言代码分析的问题,动态分析的效果受到可用测试输入的限制,而模糊测试技术则可以有效的缓解这一问题。

然而,现有的模糊测试技术大多都针对单一编程语言的软件,主要集中在 C/C++ 上。将这些单一语言的模糊测试工具应用于多语言代码(例如,通过对入口语言单元进行模糊测试),本质上将其他语言单元视为黑盒测试,忽略了跨语言交互,从而大大削弱了模糊测试的潜力。

因此,本文提出了对多语言代码进行模糊测试的方案,以增强在现实世界的多语言系统中系统性地发现漏洞的能力。为了在可扩展性和有效性之间取得良好平衡,作者关注灰盒模糊测试,提出了PolyFuzz,一种整体的多语言系统灰盒模糊测试框架,并提出了以下方法:

  (1)利用敏感性分析来显式地建立输入和分支谓词之间的语义关系回归模型。通过自适应地切换模糊测试模式和学习模式(进行种子生成),实现全面的模糊测试,并且捕捉跨语言信息流。

  (2)采用最少的特定语言分析来进行全面的覆盖率测量,实现了一个自定义的中间语言表示(SAIR),收集学习回归模型所需要的变量值,实现对异构语言的运行时探测,使得PolyFuzz的其他部分实现了语言无关模糊测试。

二、 PolyFuzz整体框架

PolyFuzz的基本框架如下图所示,主要由三个阶段组成:

01

静态分析和插桩

02

敏感分析与种子生成(Sensitivity analysis & seed generation)

03

核心模糊测试

PolyFuzz需要两个输入:待测程序P和初始种子。其基本流程为:在第一阶段将P的每个函数翻译为语言无关的中间语言表示,提取分支变量约束,对P进行插桩;在第二阶段生成更有针对性的种子,第二阶段包含两种模式:模糊测试模式(fuzzing mode)和学习模式(learning mode),当第二阶段的动态事件监控器检测到内存数据库中的分支变量更新,第二阶段即从模糊测试模式切换到学习模式,通过对种子输入和分支变量的回归模型训练学习,进行敏感分析,生成与分支变量关联度更高的种子,切换回fuzzing mode继续进行模糊测试;第三个阶段进行路径覆盖引导的核心模糊测试,产生结果输出。

图 1 PolyFuzz整体框架

三、PolyFuzz各模块详细实现

阶段1:静态分析与插桩

阶段一主要由以下三个部分组成:

(1)IR翻译

作者为了后续的敏感分析,实现了一个自定义IR(SAIR,Sentivity Analysis IR)。SAIR的形式语法如下:

一个程序P是函数定义序列F;函数F包括返回类型τ,函数名f,参数序列x和语句序列S;返回类型有两种,整数I和其他O;语句S包括两种类型,非比较语句(调用、分配和返回等)和分支谓词语句(两个变量的比较等);表达式e有三种类型,带有类型τ的变量x、常量C和空字符串ε。

翻译过程通过针对每个基本块进行,并且记录每个基本块的前驱和后继来构建控制流图。算法如下图2所示。

(2)插桩引导计算

基于SAIR翻译的结果,作者提出计算最小化的插桩位置。通过删除控制流图中的支配基本块与后支配基本块实现最小化插桩。算法如下图3所示。以图4为例,翻译阶段会首先将源代码翻译为SAIR,之后转换为块级控制流图,在插桩计算的过程中,由于B3块支配B4和B5,B6后支配B2,B4和B5,因此不对B3和B6块进行插桩。在这张控制流图中,包含三条路径为{B1B2B6, B1B3B4B6, B1B3B5B6},这三条路径在删除B3块和B6块之后仍然可以被识别,因此证明了本文提出的最小化插桩是可以进行路径区分的。

(3)静态/动态插桩

在基于第二部计算出的最小化插桩位置的基础上进行插桩,对于编译语言进行静态插桩(C),对于解释型语言(Python)进行动态插桩。

图 2 翻译给定函数为SAIR

图 3 计算插桩引导

图 4 翻译语言单元为SAIR和块级CFG示例

阶段2:敏感分析和种子生成

这个阶段解决的主要问题是如何在灰盒测试中生成优质的种子,包括两个模式:fuzzing mode和learning mode,主要包括四个部分:

(1)动态事件监控

阶段2从fuzzing mode开始,针对第一阶段生成的插桩程序P’进行模糊测试,同时将过程中产生的动态事件记录到shadow event queue中,监控器从队列中提取事件信息存储到内存数据库中,并检查是否覆盖了新的分支变量,如果产生了改变,则切换模式进入到learning mode中。

(2)种子划分与采样

分支变量更可能通过输入的某一块对分支输出结果产生影响,因此作者将种子划分为定长的种子块(整型长度:1,2,4,8,16字节)进行变异以实现更好的测试效果。在对种子进行分块之后,PolyFuzz对每个种子块的取值范围进行采样:对当前的值进行随机变异,之后重新运行P’,利用动态事件监控器监控分支变量值发生的变化,最终生成SBP(seed block, branch variable)列表存储在内存数据库中。算法如下图所示。

图 5 种子划分与采样算法

(3)分支输入回归模型

作者采用了一种近似语义计算的模型来对程序输入和分支变量之间的关系进行学习,通过对当前内存数据库中存储的SBP进行回归模型的训练来捕获分支变量和种子块之间的关系。之后通过回归模型对扩展后的相应分支约束值进行预测而生成新的种子块,算法如下图所示。

图 6 分支输入回归模型算法

扩展约束常量是基于第一阶段中收集到的约束常量的基础上进行,对相应的分支变量进行采样选择不同的值从而导致程序运行不同的路径。训练的过程中将当前的SBP数据的80%用作训练集,剩余数据用作测试集。通过三种回归模型进行训练,选择效果最好的一个模型对SBP种子块进行预测。

(4)种子生成

PolyFuzz通过聚合回归模型生成的种子块生成新的种子。作者通过将种子生成问题转变为路径构建解决新种子存在取值范围过大的问题。如下图,对于一个给定的种子,首先添加开始节点S和结束节点E将其表示为有向图,将每个种子块中的值进行相应的连接所生成的一条路径即为一个新的种子,基于此构想,种子生成主要由以下两个子步骤完成:

图 7 种子的图表示

(a)加权采样。针对每个种子块中的值依据每个块覆盖到的分支变量个数对其值域进行加权采样。针对每个种子块中采样值个数的计算公式如下。

(b)路径构建。通过深度优先遍历种子图,结合预先计算好的采样权重,随机采样种子块的值,迭代构建路径,生成种子。算法如下图。

图 8 路径构建和种子组合算法

阶段3:核心Fuzzing

在此阶段,PolyFuzz进行传统的模糊测试(包括种子选择、变异和bug报告等)。PolyFuzz将不同的语言单元中的所有基本块信息映射到相同的共享内存字节图中,实现在不知道测试程序使用语言的情况下计算块和路径覆盖,使得fuzz的阶段与语言无关。

四、实现与局限

作者利用PolyFuzz实现了针对Python,Java和C的组合编程程序的测试,图6展示了PolyFuzz的关键组件,其中包含了一个通用的C组件(SAIR解析器、插桩指导计算IGC、DynTrace)进行静态分析和插桩。特定语言的分析层可以通过语言接口层中的包装器来使用组件中的库。实现第二阶段的组件是SASG,完成敏感性分析和种子生成。第三阶段的模糊测试使用AFL++实现。

图 9 POLYFUZZ实现概述

特定语言分析利用翻译器总结每个变量的类型,同时识别分支语句和分支变量;特定语言的插桩通过基于SAIR翻译的IGC模块指导,探测基本块和分支变量信息。

局限:PolyFuzz使用非持久模式,为每次模糊测试执行fork一个新的进程,可能会影响目标程序的执行效率,降低模糊测试的效率。且目前PolyFuzz目前的实现假设模糊测试的目标程序在单个进程中运行,难以触发跨进程漏洞。

五、实验评估

作者通过以下三个研究问题进行实验对PolyFuzz的效果进行评估:

RQ1: PolyFuzz在真实世界中的多语言程序上的表现相较于目前的SOTA效果如何?

RQ2: PolyFuzz在单语言程序上的表现相较于SOTA效果如何?

RQ3: PolyFuzz中的敏感分析与种子生成的效果如何?

(一)实验准备

1.实验环境

64位Ubuntu 18.04;

32核心CPU(AMD Ryzen Threadripper 3970X);

256 GB内存;

测试进行24小时;重复实验5次。

2.基准模糊测试器

(1)Google OSS-Fuzz框架中的三个SOTA单语言工具:Honggfuzz(C),Jazzer(Javs),Atheris(Python)

(2)对Jazzer和Atheris进行扩展得到Jazzer-C-ext和Atheris-C-ext(扩展对C语言的覆盖测量)

(3)禁用PolyFuzz中的SASG模块(POLYFUZZ-NSA)

(4)AFL++

3.评估基准和初始输入种子

(1)15个真实世界的多语言系统(10个Python-C,5个Java-C)(下表1);

(2)从Google OSS-fuzz中选取了15个真实世界的单语言系统(下表2);

(3)为15个多语言系统开发了 POLYFUZZ 的新驱动程序,而对于10个 Python-C 程序和5个 Java-C 程序,分别为 Atheris 和 Jazzer 开发了新的驱动程序;

(4)为15个单语言程序,对所有单语言模糊测试工具重用了 Oss-Fuzz 中的驱动程序,并为 POLYFUZZ 开发了新的驱动程序。

4.评估基准和初始输入种子

(1)15个真实世界的多语言系统(10个Python-C,5个Java-C)(下表1);

(2)从Google OSS-fuzz中选取了15个真实世界的单语言系统(下表2);

(3)为15个多语言系统开发了 POLYFUZZ 的新驱动程序,而对于10个 Python-C 程序和5个 Java-C 程序,分别为 Atheris 和 Jazzer 开发了新的驱动程序;

(4)为15个单语言程序,对所有单语言模糊测试工具重用了 Oss-Fuzz 中的驱动程序,并为 POLYFUZZ 开发了新的驱动程序。

5.性能指标

选取基本块覆盖、路径覆盖和触发的bug数量作为性能指标。

RQ1: 针对多语言系统模糊测试的性能评估

表3展示了PolyFuzz与Atheris和Atheris-C-ext在10个Python-C程序上的性能对比,表4展示了PolyFuzz与Jazzer和Jazzer-C-ext在5个Java-C程序上的性能对比。

PoyFuzz在整个系统中覆盖的基本块比Atheris-C-ext多36.7%,在Python单元中覆盖的基本块比Atheris多52.3%。与Jazzer相比,在整个系统中覆盖的基本块和在可比较的语言单元中覆盖的基本块分别增加了25.3%和29.1%(见表4)。这些结果表明,POLYFUZZ显著提高了代码覆盖率。Atheris在Pillow项目中触发了1个漏洞,而Atheris-C-ext进一步在Bottleneck中触发了2个漏洞,Jazzer和Jazzer-C-ext均无法发现任何漏洞。而POLYFUZZ成功地在6个项目中触发了12个漏洞,其中包括11个Python-C和1个Java-C程序。且作者对这12个漏洞确认并开发了PoC成功进行复现。因此证明:POLYFUZZ中的整个系统覆盖感知不仅促进了模糊测试过程的演化以获得高代码覆盖率,还增加了在真实的多语言项目中发现漏洞的可能性。

作者对实验结果进行分析:相比之下,POLYFUZZ利用整个系统的覆盖范围作为反馈,因此在发生C单元中的覆盖变化时,它可以识别更多被单语言模糊测试工具忽略的有利种子。此外,通过突变这些种子,POLYFUZZ能够更有效地变异,并促进整个系统的覆盖率提高,从而实现比Atheris和Jazzer更高的覆盖率。此外,通过学习回归模型来捕捉分支变量和种子块之间的语义关系,POLYFUZZ能够生成更有效的种子,以在两个方向上激活相关分支谓词,从而更进一步地探索更多分支和路径。正因为如此,POLYFUZZ还能够比Atheris-C-ext和Jazzer-C-ext覆盖更多的基本块。

表 1 15个真实世界的多语言系统

表 2 15个单语言系统(Google OSS-fuzz)

表 3 PolyFuzz, Atheris, Atheris-C-ext在Pytho-C基准上的性能比较

表 4 PolyFuzz, Jazzer, Jazzer-C-ext在Java-C基准上的性能比较

RQ2: 针对单语言系统模糊测试的性能评估

将POLYFUZZ 与 Atheris、Jazzer 和 Honggfuzz 在15个真实单语言基准测试中进行比较。如表5-7所示,在这些单语言基准测试中,POLYFUZZ的基本块覆盖率分别比Atheris、Jazzer和Honggfuzz高出20.1%、11.0%和10.1%。作者分析实验结果原因是由于POLYFUZZ 的 SASG 模块可以从程序的常数分支约束条件中有效地生成种子。此外,通过使用生成的种子作为输入,POLYFUZZ 可以用更少的随机突变覆盖更多的块。总体而言,POLYFUZZ 能够通过进一步突变这些种子发现更多有利的种子,这些种子是由与分支变量强相关的种子块生成的。

POLYFUZZ成功触发了2个新漏洞,包括Python基准测试Pyyaml中的1个递归错误和Java基准测试Javaparser中的1个JVM挂起。对于Pyyaml中的这个漏洞,Atheris也使用不同的种子输入报告了类似的问题。作者开发了PoC实现了漏洞复现。

表 5 PolyFuzz与Atheris在Python benchmark上的性能比较

表 6 PolyFuzz与Jazzer在Java benchmark上的性能比较

表 7 PolyFuzz与Honggfuzz在C benchmark上的性能比较

RQ3: 针对PolyFuzz中SASG模块的性能评估

作者比较了PolyFuzz和PolyFuzz-NSA在15个多语言程序上的性能,结果如表8所示,比较了PolyFuzz和AFL++在5个C基准上的性能,结果如表9所示。

在基本块和路径覆盖率方面,POLYFUZZ明显优于POLYFUZZ-NSA。例如,对于Pillow基准测试,POLYFUZZ比POLYFUZZ-NSA覆盖了320个(增加了30.7%)基本块和86个(增加了58.5%)路径。总的来说,POLYFUZZ的基本块和路径覆盖率分别比POLYFUZZ-NSA高出17.4%和21.8%。这些结果表明,POLYFUZZ中的敏感分析和种子生成技术对PolyFuzz的效果起到了重要的作用。但PolyFuzz-NSA相较于单语言fuzzer相比覆盖了更多的基本块。具体而言,POLYFUZZ-NSA比Atheris覆盖了30.5%更多的Python基本块(而POLYFUZZ为52.3%),比Jazzer覆盖了19.5%更多的Java基本块(而POLYFUZZ为29.1%)。与C基准测试上的AFL++相比,采用相同的覆盖反馈机制,POLYFUZZ覆盖的基本块和路径分别比AFL ++多出7.6%和11.4%,可以证明SASG在PolyFuzz也能在单语言测试中有良好的表现。

同时,在bug触发方面,POLYFUZZ-NSA仅触发了POLYFUZZ检测到的12个漏洞中的4个。可以证明,SASG显著提高了POLYFUZZ的漏洞发现能力。

关于覆盖率增长的研究:图7显示了在Pillow上进行的5次24小时实验中 #basic blocks 和 #paths 覆盖率的增长趋势。PolyFuzz最初表现略弱,因为SASG在观察到新的分支变量覆盖时可能会迅速进入学习模式,此时对于SASG来说任何分支变量都是新的。在这些分支变量的种子分区和抽样期间,核心模糊测试必须保持空闲状态。一旦第一次回归学习通过,基本块和路径数量就可以基于已学习的种子快速增长。相比之下,POLYFUZZ-NSA在运行12小时后会停止增长。

图 10 Pillow程序上平均基本块覆盖和路径覆盖的趋势变化

表 8 PolyFuzz和PolyFuzz-NSA在15个多语言程序上的性能比较

表 9 PolyFuzz和AFL++在C benchmark上的性能比较

六、 讨论

PolyFuzz的贡献是针对多语言软件产生的覆盖率的测量和反馈,利用敏感性分析来建模输入和整数分支变量之间的关系;然后以扩展的常数分支约束作为输入,它生成新的种子来有效地命中或反向各自的分支。新覆盖的代码可以触发新一轮的种子学习,增强在模糊期间持续的覆盖增长。

在实际操作过程中,一些因素会限制PolyFuzz的效果:在真实程序中非整形的变量也占有相当大的比重,目前PolyFuzz对这类型的变量没有很好的处理效果。其次,当输入和分支变量之间的相关性过于复杂而无法使用回归模型进行建模时,SASG模块就会失去意义。第三,种子分区和抽样阶段中,随机变异过程中的执行路径可能会发生变化,影响分支变量的覆盖率,这可能导致无法收集足够的数据来训练回归模型。最后,长种子可能会限制模糊测试效果,因为它可能使POLYFUZZ在种子抽样中停留时间过长,在此期间核心模糊测试处于禁用状态。

处理非整型分支变量可能会更加复杂,这些变量上存在两个关键挑战,阻碍了SASG对它们的处理:(1)如何统一不同语言中的数据表示?数据表示的差异可能会防止程序的有效插桩。(2)什么样的数据存储方式是高效的?

对于无法学习分支变量和种子块之间相关性的情况,作者对一些分支变量进行了手动验证的抽样,发现这样的变量主要分为两类:(1)函数返回值,只取特定值(例如0或-1);(2)从配置中获取的值(例如从环境变量或配置文件中读取)。如何自动地将这些配置更改纳入模糊测试是一个值得研究的课题。

七、总结

本文提出了PolyFuzz,一个新颖的多语言软件整体灰盒模糊测试框架。PolyFuzz通过为模糊测试专门设计的自定义中间表示SAIR,在语言无关的情况下测量整个系统块和路径覆盖率。除了整体覆盖反馈外,作者还通过回归建模种子与分支变量之间的语义关系有效地生成新种子。实验结果显示,相比最先进的单语言模糊测试工具,POLYFUZZ具有显着优势。


文章来源: https://mp.weixin.qq.com/s?__biz=MzU1NTEzODc3MQ==&mid=2247485900&idx=1&sn=9c94c849b8d472cacf36942b97addb80&chksm=fbd9a270ccae2b6622cc91ab45ab21dbc534ce33dc269b6d3935760c4e09403f7237f56ffb5a&scene=58&subscene=0#rd
如有侵权请联系:admin#unsafe.sh