Compositional Fuzzing Aided by Targeted Symbolic Execution

题目:Compositional Fuzzing Aided by Targeted Symbolic Execution

作者:Saahil Ognawala, Fabian Kilger, Alexander Pretschner

单位:Faculty of Informatics, Technical University of Munich,Boltzmannstr. 3, 85748 Garching, Germany

邮箱: saahil.ognawala@tum.de (Saahil Ognawala)

出版:preprint

背景:

当前的基于覆盖的fuzz技术,可以有效地找到浅层的bug,但是无法在更深层次的程序中实现高覆盖率,找到需要满足很多分支条件的复杂bug。

白盒技术,比如符号执行/混合执行或者白盒模糊,因为路径爆炸问题,导致执行在浅层分支就卡住。

组合分析是一种减轻符号执行中的路径爆炸问题的技术,通过分析系统的各个组件,修剪程序中不会触发组件漏洞的路径。
相比传统符号执行,组合分析能够覆盖程序的更多部分,并且发现了程序中的更多漏洞。

但是目前还没有将组合分析用于fuzz的工作。

目标:

通过提高单一函数的覆盖率来提高模糊器的漏洞检测能力。我们将单一函数定义为参数不为空的函数,即参数是int a等,不可以是void。 我们的中心前提是,提高单一函数的覆盖率可以发现更多的漏洞。 然后,采用导向符号执行来对由模糊测试发现的漏洞执行可达性分析,以确认漏洞是否在从更高级别的调用上下文中可达。

方案:

在本文中,我们介绍Wildfire,一种新颖的开源组合模糊框架,实现了用于fuzz大型真实程序的组合分析方法。

首先,使用自动生成的种子输入来fuzz单一函数(覆盖程序调用图的所有深度的函数);

然后总结了fuzzer在单一函数中发现的漏洞,并用摘要进行函数替换;

最后,使用导向符号执行来确定单一函数中的漏洞是否可以由其Caller(调用)函数触发,重复迭代直到顶级函数,例如main,完成利用这些漏洞的可达性确认。

Contribution:

1.提出了一个新颖的结合fuzzing和targeted符号执行的compositional fuzzing技术,用来找到程序中的漏洞。

2.设计一个fuzzing driver,可以自动生成种子(driver的作用就是将种子中数据取出,传递给函数参数)

3.框架的设计与实现可以有效地使用多核并行化,使得在寻找大规模程序漏洞的时候更加scalable和efficient。

4.实验证明,Wildfire相比SE和fuzz可以找到更多的漏洞,有更高的覆盖和更低的时间消耗。可以复现开源库中几乎所有的可利用漏洞,并且发现新的漏洞。

5.Wildfire已经被开源实现,作为第一个混合fuzz和SE的compositional分析工具,可以分析C程序。

Design

Overview

Wildfire的workflow如下:

1.Instrumentation and compilation.

将源码编译成LLVM IR。

2.Generating fuzzing drivers.

为了fuzz程序中的单独函数,Wildfire需要生成fuzzing driver,作为单独函数的wrapper:

1.给定参数列表,为函数生成种子输入(seed-arguments)

2.将fuzzer生成的输入正确分配给函数参数。

2.1-Seed-argument Generation

静态地为单独函数生成种子参数。

两种方法:

1)生成两个字节流{一个空流和一个由英文字母中的随机字符组成的流,[a-z]}。对于指针类型的参数,具有空字节流的缓冲区相比于非空字节流更可能导致函数崩溃。

2)生成包含与函数参数相等数目分隔符的字节流(分隔符确定字节流中提取某参数的开始和结束)。

2.2-Argument Extraction

由fuzzer提供的输入(种子参数)是字节流。我们需要从该字节流中提取字节分配给函数参数。分两种情形: 非指针和指针数据类型。我们将忽略具有双指针或多指针的参数的任何函数-limit。

算法1: 提取非指针类型参数,例如int或char。ExtractFixedSizedArgument函数只是将typeSize字节数从字节流复制到参数,并将其转换为所需类型。如果要消耗的流中不足typeSize字节数,则使用空字符字节填充流。

算法2: 提取指针类型的参数,基于字节流中的特殊分隔符(假设为 //),它将字节流分割以分配给动态大小的参数类型。对于每个指针类型参数,ExtractDynamicSizedArgument将输入字节流复制到t,直到遇到第一个分隔符,或者到达字节流结束。辅助函数LookForDelimiter返回剩下的字节流中的第一个分隔符位置,或者流的结尾.RoundDown将给定的大小舍入为typeSize的倍数。

note:对于算法2,

如果说接收的是指数型参数,那么分隔符是//,首先在remData中找到//的位置,如果找到返回该位置,否则返回remData的结尾位置,记为givenSize。

如果是指针,并不知道这个是几重指针,那么将需要RoundDown来对givenSize进行除以typeSize下取整,得到bufSize。取remData中的前bufSize数据到t中。然后如果bufSize小于等于remSize,就把givenSize之后的remData部分放回remData中(就是//之后的部分),否则将remDat置为空。

3.Generating test-cases with fuzzing.

将fuzzer应用于单一函数,生成种子参数。 Fuzzer的目标是上面生成的驱动程序(被包装后的单独函数),它可以由fuzzer直接执行。 对于所有单一函数,此步骤可以并行。
对于所有单独函数,此步骤的输出是一个list Tf,包含由函数f的fuzzer生成的所有测试用例。

Tf = {t1 , t2 , . . . tn}, (1)

其中n是fuzzer生成的唯一测试用例的数量。 如果函数f接受m个参数,那么,

ti =<Ii1 , Ii2 , . . . Iim>, (2)

其中Iik是第k个参数。

4.Generating crash reports.

通过在轻量级的程序版本(a lightweight compiled object?)上重放由fuzzer生成的测试用例,获取单独函数的模糊测试报告,包含函数参数和崩溃执行的堆栈跟踪等详细执行信息。

步骤:

1-使用 -cmin 和 -tmin 最小化测试用例集合.

2-将测试用例集传递给编译对象的检测版本,检测缓冲区溢出,空指针解引用和其他索引越界内存操作等漏洞

输出:

1-导致程序崩溃的所有测试用例(参数)的列表Tf'。

2-单一函数所报告崩溃的堆栈跟踪。

带有参数ti的函数f0,其崩溃执行的堆栈跟踪Sf0,是一个有序集合。

其中fi调用了fi+1,L返回可能有漏洞的函数名和指令(行号)。

5.Summarizing functions.

用触发漏洞的参数赋值集替换vulnerable函数中的路径集。
对于vulnerable function f0,其参数列表P:

P=<p1,p2,...pm>, (4)

其中pi是f0的第i个参数。为f0生成的摘要fsummary如Algorithm 3所示。


P==ti定义为

P≠ti就是取反。

函数摘要原理:将形式参数与触发漏洞的具体参数值进行比较。对于指针数据类型,需要使用memcmp比较分配的内存内容。

1-当找到P的匹配等于ti时,Wildfire会报告断言错误,说明f中的漏洞可以从f的调用函数触发。

2-如果没有断言错误,就调用原始函数f继续处理。

limit-由于Fuzzer仅生成具体输入而不是一般路径约束,所以只能匹配参数的具体值。(有漏报)

6.Determining Feasibility of Reported Vulnerabilities

使用堆栈跟踪匹配和目标符号执行确定已发现漏洞的可行性分析,以递归方式生成触发漏洞的具体参数,直到main函数或API。

原理:如果函数f中的漏洞可以从调用f的函数中利用,并且可以从调用调用f函数的函数中利用……直到接口函数,那么函数f中的漏洞是被确认可利用的。

Phase 1: Stack-trace Matching
根据第四步的崩溃报告,可以获得单独函数崩溃的堆栈跟踪。 如果堆栈跟踪Sfb是Sfa的有序子集,则fb中的漏洞可以被fa利用,即匹配到Sfa和Sfb。函数fa的崩溃是由于调用了函数fb,并传递给fb中参数,触发了fb中的vulnerable指令,导致崩溃。

Phase 2: Targeted Symbolic Execution
如果没有为Sfa找到这样的匹配堆栈跟踪,可能是因为fuzzer无法生成触发所述路径的测试用例,而不是因为这样的路径不存在。

这种情况下,Wildfire利用符号执行来调用vulnerable函数,有针对性地检测phase1中找不到匹配堆栈跟踪的vulnerable函数。

该阶段将函数摘要替换函数体,从所有的父函数到vulnerable函数进行有针对性的符号执行。再次考虑一个易受攻击的函数fa,第1阶段没有找到任何匹配的堆栈跟踪。使用目标搜索策略时,将忽略所有无法到达目标的分支(静态分析)。

Wildfire依次应用阶段1和阶段2,直到达到main函数或库的API函数。 此阶段的输出是漏洞报告列表,每个漏洞报告都包含易受攻击的指令,函数和函数链,通过这些可以利用这些漏洞。

Implementation

AFL for Fuzzing.

ASAN for Generating Crash Reports.

获取由非法内存相关操作(如缓冲区溢出或后续使用)引起的崩溃报告。

KLEE22 for Symbolic Execution.

符号执行工具是KLEE22,它以程序中的任意函数调用为目标,进行路径探索。

原文地址:https://www.cnblogs.com/linkJ/p/13591703.html