Directed Greybox Fuzzing 论文翻译

定向灰盒测试 AFLGO

摘要:现有的Greybox Fuzzers (GF)不能有效地定向,例如,针对有问题的更改或补丁,针对关键的系统调用或危险的位置,或者针对我们希望重现的漏洞报告的堆栈跟踪功能。在本文中,我们引入了定向灰盒模糊(DGF),它产生的输入目标是有效地到达给定的目标程序位置集合。我们开发并评估了一个模拟的基于退火的能量调度计划,该计划逐渐将更多的能量分配给靠近目标位置的种子,同时减少远离目标位置的种子的能量。用我们实现的AFLGo进行的实验表明,DGF优于基于定向符号执行的白盒模糊测试和无向灰盒模糊测试。我们展示了DGF在补丁测试和崩溃再现中的应用,并讨论了将AFLGo集成到Google的连续模糊测试平台OSS-Fuzz中。由于它的指导性,AFLGo可以在LibXML2等几个模糊的、安全关键的项目中发现39个bug。17个CEV被申请。

关键词:补丁测试;崩溃重现可达性;定向测试;基于覆盖率的灰盒模糊分析;验证真阳性。

1引言:灰盒模糊检测被认为是漏洞检测的前沿技术。GF使用轻量级的仪器来确定输入执行的路径的唯一标识符,而性能开销可以忽略不计。通过改变提供的种子输入来生成新的输入,如果它们执行了新的有趣的路径,则将其添加到fuzzer的队列中。AFL[43]负责发现数百个高影响漏洞[42],已被证明可以“凭空”生成有效的图像文件[41],并且有大量安全研究人员参与扩展它。

    然而,现有的灰盒模糊器不能有效地定向。定向模糊器是安全研究人员投资组合中的重要工具。与无向模糊器不同,定向模糊器将大部分时间预算用于到达特定的目标位置,而不会浪费资源来强调不相关的程序组件。定向模糊器的典型应用包括:

  1. 通过将变更语句设置为目标进行补丁测试。当一个关键组件被更改时,我们希望检查这是否引入了任何漏洞。图1显示了引入Heartbleed的提交[49]。专注于这些变化的模糊器有更高的机会暴露漏洞。
  2. 通过在堆栈跟踪中设置方法调用作为目标来再现崩溃。当报告现场崩溃时,只有堆栈跟踪和一些环境参数被发送到内部开发团队。为了保护用户的隐私,特定的崩溃输入通常是不可用的。定向模糊器允许内部团队快速重现此类崩溃。
  3. 静态分析通过将语句设置为静态分析工具报告为潜在危险的目标来验证报告[9]。在图1中,工具可能将第1480行定位为潜在的缓冲区溢出。定向模糊器可以生成测试输入来显示漏洞,如果它确实存在的话。
  4. 以敏感源和敏感接收器为目标的信息流检测[22]。为了暴露数据泄漏漏洞,安全研究人员希望生成执行用例,以执行包含私人信息的敏感源和敏感接收器,其中数据对外部世界可见。定向模糊器可以用来有效地生成这样的执行用例。

    大多数现有的定向模糊器都是基于符号执行[4,9,15,20,21,27,34,66]。符号执行是一种白盒模糊测试技术,它使用程序分析和约束求解来综合执行不同程序路径的输入。为了实现定向模糊器,符号执行由于其系统路径的探索而一直是首选的技术。假设,在控制流图中存在一条到目标位置的路径π。符号执行引擎可以构造一个路径条件,即一个一级逻辑公式φ(π),该公式由所有执行π的输入组成。如果路径约束是可满足的,则可满足模理论(SMT)求解器生成一个实际输入t作为路径约束φ(π)的解。因此,输入t 执行包含目标的路径π。

图1 提交引入Heartbleed:在从传入消息p(1455-8)中读取负载后,它将负载从传入消息复制许多字节到传出消息。如果有效载荷设置为64kb,并且传入的消息长度为1字节,则发送方将显示多达~ 64kb的私人数据。

    定向符号执行(direct symbolic execution, DSE)将可达性问题转化为迭代约束满足问题。由于大多数路径实际上是不可行的,搜索通常通过寻找到中间目标的可行路径来进行。例如,补丁测试工具Katch[21]使用符号执行引擎Klee[7]来获取已更改的语句。假设,我们将图1中的Line 1480作为目标,Katch找到一条可行路径π0到达Line 1465作为中间目标。接下来,Katch将约束φ(π0)∧(hbtype == TLS1_HB_REQUEST)传递给约束求解器,以生成一个实际执行1480行中目标位置的输入。与灰盒模糊器不同,基于符号执行的白盒模糊器提供了一个简单的句柄来实现定向模糊。

    然而,DSE的有效性是以效率为代价的[5]。DSE花了相当多的时间进行重量级的程序分析和约束求解。在每次迭代中,DSE使用程序分析识别那些需要被否定以接近目标的分支,从沿着这些路径的指令序列构造相应的路径条件,并使用约束求解器检查这些条件的可满足性。在DSE生成单个输入的同时,灰盒模糊器可以执行多个数量级的输入。这为我们提供了一个开发轻量级和定向灰盒模糊器的机会。从相同的种子开始,当指向图1中的提交时,我们的定向灰盒模糊器AFLGo只需要不到20分钟就可以暴露Heartbleed,而DSE工具Katch[21]即使在24小时内也不能暴露Heartbleed。

    在本文中,我们介绍了定向灰盒模糊(DGF),它的重点是在程序中到达一组给定的目标位置。在高层次上,我们将可达性作为一个优化问题,并使用特定的元启发式来最小化生成的种子到目标的距离。为了计算种子距离,我们首先计算并测量每个基本块到目标的距离。虽然种子距离是程序间的,但我们的新方法只需要对调用图和每个程序内CFG进行一次分析。在运行时,模糊器会汇总每个基本区块的距离值,计算出种子距离的平均值。DGF用于最小化种子距离的元启发式算法称为模拟退火[19],并以能量调度的形式实现。一个能量调度控制着所有种子的能量[6]。一个种子的能量是用来模糊测试它的时间。与所有灰盒模糊测试技术一样,通过将分析移至编译时,我们可以最小化运行时的开销。

DGF将目标位置的可达性作为优化问题,而现有的定向(白盒)模糊方法将可达性作为迭代约束满足问题。

    我们的实验表明,定向灰盒模糊在有效性(即DGF暴露更多漏洞)和效率(即DGF在同一时间内达到更多目标)方面优于定向符号执行。然而,两种技术的集成比单独使用一种技术的性能更好。我们在流行且非常成功的灰盒模糊器AFL中实现了DGF[43],并将我们的定向灰盒模糊器称为AFLGo。对于补丁测试,我们在原始的Katch基准上将AFLGo与最先进的Katch[21](定向符号执行引擎)进行比较。AFLGo发现了13个漏洞(7个cve),这些漏洞是Katch无法暴露的,并且AFLGo可以在同一时间内比Katch多覆盖13%的目标。然而,当这两种技术结合使用时,可以比单独使用多覆盖42%的目标。两种定向模糊测试方法相互补充。对于崩溃再现,我们将AFLGo与最先进的BugRedux[18]进行比较,BugRedux是原始BugRedux基准上的定向符号执行引擎。当只有堆栈跟踪中的方法调用可用时,AFLGo可以再现比BugRedux多三倍的崩溃。实验表明,基于退火的能量调度是有效的,AFLGo是有效的。我们将AFLGo与实现AFLGo的无向灰盒模糊器AFL进行了比较。实际上,AFLGo在测试LibPNG时可以比AFL快3到11倍,在测试Binutils时比AFL快1.5到2倍。

定向灰盒模糊测试是一种有效的定向模糊测试,是基于符号执行的定向模糊测试的有效补充。

AFLGo是一个有用的补丁测试工具,可以有效地暴露最近引入的漏洞和以前报告漏洞的不完整修复。

    我们的实验表明,定向灰盒模糊在补丁测试和崩溃再现领域是有用的。我们还将AFLGo集成到OSS-Fuzz[58]中,OSS-Fuzz是一个针对安全关键库和其他开源项目的持续测试平台,最近在Google宣布[44]。我们与AFLGo的集成在安全关键库中发现了26个以前未发现的漏洞,其中10个是分配给cve的严重漏洞。大多数发现都可以直接归功于AFLGo的指导。

    本文的主要贡献是:

•灰盒模糊和模拟退火的集成,

•程序间距离的度量公式,一次考虑多个目标,可以在模糊时有效地预先计算,并在运行时有效地推导,

•定向灰盒模糊作为AFLGo的实现,该AFLGo可在https://github.com/aflgo/aflgo公开获得,

•将AFLGo作为补丁测试工具集成到OSS-Fuzz的全自动工具链中,该工具可在https://github.com/aflgo/oss-fuzz上公开获取,

•大规模评估定向灰盒模糊测试作为补丁测试和崩溃再现工具的有效性和效率。

    本文其余部分的结构如下。在第2节中,我们使用心脏滴血作为示例案例研究来解释定向灰盒模糊的相关特征。在第3节中,我们讨论距离的公式化度量以及灰盒模糊与模拟退火的集成。在第4节中,我们介绍了我们的实现以及实验设计。在第5节中,我们将AFLGo应用于补丁测试,并将其与最新技术进行比较(Katch[21])。在第6节中,我们将讨论将AFLGo集成到OSS-Fuzz中,其中它针对的是最近的更改。在第7节中,我们将AFLGo应用于崩溃再现,并将其与基线无向灰盒模糊器(AFL)和最先进的(BugRedux[18])进行比较。第8节引出了对有效性的威胁。在第9节中对相关工作的调查之后,我们在第10节中得出结论。

2 动机样例:我们使用心脏滴血漏洞作为案例研究和动机样例来讨论两种不同的定向模糊方法。传统上,定向模糊测试是基于符号执行的。在这里,我们将基于符号执行引擎Klee[7]的补丁测试工具Katch[21]与我们在本文中提出的定向灰盒模糊测试实现AFLGo进行比较。

2.1 心脏滴血与补丁测试:心脏出血[49](CVE-2014-0160,)是一个漏洞,它损害了通过所谓安全协议(SSL/TLS)发送的数据的隐私性和完整性。 引入 Heartbleed [46] 的提交摘录如图 1 所示。 有趣的是,Heartbleed 可以在没有中间人(MITM)的情况下被利用。 假设 Bob 有一个秘密,而攻击者 Mallory 想要知道这个秘密。 首先,Bob 从 Mallory 接收的信息中读取信息类型和有效载荷。 如果信息属于某种类型,Bob 就会将传出信息的类型和有效载荷设置为自己的响应。 最后,Bob 从接收信息 (pl) 中复制许多字节的有效载荷到传出信息 (bp)。 如果分配给 pl 的有效载荷字节数不足,Bob 就会泄露自己的秘密。 心脏出血在被引入 OpenSSL 库两年后才被发现,这导致了漏洞的广泛传播。 截至 2016 年 4 月,仍有 25 万台机器存在漏洞[61]。

    Heartbleed是在2011年新年前夕由commit 4817504d引入的,它实现了一个名为Heartbeat的新功能。定向模糊器将改变的语句作为目标位置,可能在漏洞引入时就发现了漏洞[2],从而阻止了漏洞的广泛传播。现在,OpenSSL由近50万行代码组成[47];引入该漏洞的提交增加了500多行代码[46]。可以说,以一种无导向的方式对所有OpenSSL进行模糊测试,而实际上只有最近的更改被认为是容易出错的,这将是对资源的浪费。定向模糊器会更有效地执行这些变化。大多数补丁测试工具都是基于定向符号执行的,如Katch[21]、PRV[3]、MATRIX[34]、CIE[4]、DiSE[27]和Qi等人的补丁测试工具[30]。由于Katch代表了自动化补丁测试的最新技术,并且很容易获得,因此我们选择Katch作为我们的激励示例。

图2: CFG草图,显示了Katch沿着种子s执行的路径收集的分支条件φ。

2.2 模糊测试心脏出血-引入源代码提交:Katch是在Klee之上实现的最先进的补丁测试工具和定向符号执行引擎[7]。首先,OpenSSL必须被编译成LLVM 2.9字节码然后,每次处理一个已更改的基本块。对于我们的激励示例,Katch将11个已更改的基本块标识为可达的目标位置,这些位置尚未被现有的回归测试套件覆盖。对于每个目标t, Katch执行以下贪婪搜索:Katch识别回归测试套件中“最接近”t的种子s。例如,现有的种子可能执行第1465行中的分支bi,但包含不正确的消息类型(参见图2)。这个种子是接近的,因为只有一个分支需要被否定就能到达目标。现在,Katch使用程序分析 i)确定最接近目标的可执行分支bi, ii)构造路径约束Π(s) = φ(b0)∧φ(b1)∧..∧φ(bi)∧. .作为s执行的每个分支条件的结合,以及iii) 确定需要修改 s 中的哪些特定输入字节来否定 bi。在本例中,这些输入字节对消息类型进行编码。然后,Katch 在 Π 中否定 bi 的条件,得出 Π ′ = φ(b0) ∧ φ(b1) ∧ ... ∧ ¬φ(bi )。 约束条件 Π ′被传递给 Z3 可满足性模态理论(SMT)求解器 [10],以计算已确定的输入字节的具体值,从而使 bi 确实被否定。  在这种情况下,传入消息现在将包含正确的类型(第1465行),并在第1480行执行漏洞。

挑战:虽然基于定向符号执行的白盒模糊测试非常有效,但它也非常昂贵。由于重量级的程序分析,Katch需要很长时间来生成一个输入。在我们的实验中,Katch无法在24小时内检测到心脏出血(图3)。请注意,每探索一条新路径,在运行时都会重新计算距离。搜索可能是不完整的,因为解释器可能不支持每个字节码,约束求解器也可能不支持每个语言特性,比如浮点运算。贪婪搜索可能会陷入局部最优而不是全局最优,并且永远无法到达目标。由于顺序搜索,Katch错过了通过当前目标的搜索进度来通知搜索其他目标的机会; 对每个目标的搜索都要重新开始。

图3 发现漏洞的时间(TTE)

    机遇:我们的工具aflgo是一个非常有效的定向灰盒模糊器。AFLGo每秒生成并执行数千个输入,并在不到20分钟内暴露Heartbleed。AFLGo实现了我们新颖的定向灰盒模糊测试技术,在运行时几乎不需要程序分析,只需要在编译/检测时进行轻量级程序分析。AFLGo实现了基于模拟退火的全局搜索[19]。这允许我们的定向灰盒模糊器接近全局优化并最终达到一组目标。搜索应该以多快的速度接近最优值是我们技术的输入(探索时间)。AFLGo实现了并行搜索,同时搜索所有目标。种子s离目标越近,执行的目标越多,AFLGo对s的适应度评估越高。

    首先,AFLGo 执行了 OpenSSL。 Clang 的附加编译器会在编译后的二进制文件中添加经典的 AFL 和我们的 AFLGo 工具。 AFL 模糊器会告知模糊器代码覆盖率的增加,而 AFLGo 模糊器会则会告知模糊器已执行的种子与给定目标集的距离。 新颖的距离计算方法将在第 3.2 节中讨论,比 Katch 的计算方法复杂得多。 它同时考虑了所有目标,并在编译时完全确定,从而减少了运行时的开销。

    然后,AFLGo使用模拟退火对OpenSSL进行模糊分析[19]。一开始,AFLGo进入探索阶段,和AFL一样工作。在开发阶段,AFLGo随机突变提供的种子以产生许多新的输入。如果一个新的输入增加了代码覆盖率,它将被添加到要模糊化的种子集合中;否则将被丢弃。提供的和生成的种子在一个连续的循环中被模糊化。例如,AFLGo以两个种子开始:s0执行在图2中〈b0,b '〉的分支,s1执行〈b0,b1,b ''〉的分支。假设存在一条从b '到t的直接路径,但这条路径是不可行的,即不能通过输入来实现。一开始,两种种子产生的新输入数量大致相同。探索的基本原理是即使时间更长,仍探索其他路径。尽管s0“更接近”目标,但s1的“子节点”更有可能实际到达t。

    AFLGo进入开发的时间由用户指定。在我们的实验中,我们将开发时间设置为20小时,超时时间设置为24小时。在开发阶段,AFLGo从距离目标更近的种子中产生更多的新输入,基本上不会浪费宝贵的时间来模糊太远的种子。假设,在这一点上,AFLGo生成了一个种子s2,它在图2中行使分支〈b0,b1,bi,bi+1〉。在开发阶段,由于种子s2最接近目标t,因此大部分时间用于模糊s2。根据实现为能量调度的退火函数,AFLGo从探索阶段缓慢过渡到开发阶段。

3技术

    我们开发了定向灰盒模糊(DGF),这是一种漏洞检测技术,专注于到达用户定义的目标位置。DGF保留了灰盒模糊测试的效率,因为它在运行时不进行任何程序分析,因为所有程序分析都是在编译时进行的。DGF很容易并行化,因此可以在需要时分配更多的计算能力。DGF允许指定多个目标位置。

    我们定义了一种程序间距离(即种子到目标位置)的度量方法,这种方法在执行前时完全确定,并可在运行时高效计算。 虽然我们的衡量标准是程序间的,但我们的程序分析实际上是基于调用图(CG)和程序内控制流图(CFG)的程序内分析。 我们将展示与程序间分析相比,这种分析如何节省四倍时间。 CG 和 CFG 在 LLVM 编译器基础架构中随时可用。

    我们使用这种新的距离度量,定义了一种新的能量调度[6],它集成了最流行的退火函数,指数冷却调度。根据我们的距离测量,基于退火的能量调度逐渐将更多的能量分配给距离目标位置较近的种子,而减少距离目标位置较远的种子的能量。

3.1灰盒测试:我们首先解释了灰盒模糊的工作原理,并指出了在哪实现距离度量和基于退火的能量调度,以实现定向灰盒模糊。 模糊(Fuzzing)是 20 世纪 90 年代米勒等人[24]使用随机测试工具研究 UNIX 工具可靠性时创造的术语。 如今,我们根据程序分析的程度将其分为三类:黑盒模糊(black-box fuzzing)只要求执行程序[60, 62, 65]。 基于符号执行的白盒模糊[7, 8, 12]需要大量的程序分析和约束求解。 灰盒模糊介于两者之间,只使用轻量级的仪器来收集一些程序结构。 在不进行程序分析的情况下,灰盒模糊可能比白盒模糊更有效。 在获得更多内部结构信息的情况下,灰盒模糊可能比黑盒模糊更有效。

    基于覆盖率的灰盒模糊器(CGF),如AFL[43]和LibFuzzer[53],使用轻量级仪器来获取覆盖信息。例如,AFL的工具捕获基本的块转换,以及粗略的分支获取命中计数。CGF使用覆盖率信息来决定保留哪些生成的输入以进行模糊处理,哪些输入接下来要进行模糊处理,以及模糊处理多长时间。我们扩展了这种仪器,也考虑了所选种子到给定目标位置集的距离。距离计算需要在调用图和llvm中现成的程序内控制流图中找到距离目标节点的最短路径。最短路径分析采用Dijkstra算法实现[23]。

    算法 1 显示了 CGF 工作原理的算法简图。 模糊器获得一组种子输入 S,并以连续循环的方式从 S 中选择输入 s,直到超时或模糊过程中止。 选择是通过下一步选择(chooseNext)实现的。 例如,AFL 本质上是从循环队列中按添加顺序选择种子。 对于选定的种子输入 s,CGF 通过assignEnergy (第 3 行)来确定模糊 s 所产生的输入数 p。这也是实现(基于退火的)能量调度的地方。 然后,模糊器根据 mutate_input (第 5 行)中定义的变异算子随机变异 s,生成 p 个新输入。AFL 使用位翻转、简单算术、边界值以及块删除和插入策略来生成新输入。 如果生成的输入 s ′ 覆盖了一个新的分支,它就会被添加到循环队列中(第 9 行)。如果生成的输入 s ′ 使程序崩溃,它将被添加到崩溃输入集合 S✗ 中(第 7 行)。同时也是有趣的崩溃输入会被标记为唯一崩溃。

    Böhme等人[6]表明,基于覆盖率的灰盒模糊可以建模为马尔可夫链。状态i是程序中的特定路径。从状态i到状态j的转移概率pij是由模糊化执行路径i的种子生成执行路径j的种子的概率给出的。作者发现,CGF执行某些(高频)路径的频率明显高于其他路径。 平稳分布的密度描述了模糊器在经过一定次数的迭代后执行某一路径的可能性。Böhme 等人开发了一种技术,根据 AFL 的分支AFLFast 中邻域的密度调整从种子生成的模糊数量,从而使模糊器倾向于低频路径。种子s产生的模糊数也称为s的能量。种子s的能量由能量调度控制。注意,能量是马尔可夫链中一个状态的局部属性,而温度在模拟退火中是全局的。

3.2 输入种子与多个目标位置之间距离的度量

    为了计算函数间的距离,我们为函数级调用图(CG)和基本模块级程序内控制流图(CFG)中的每个节点赋值。目标函数Tf和目标基本块Tb可以从给定的源代码引用中快速识别(例如,d1_both.c:1480)。

    函数级目标距离决定了函数到调用图中所有目标函数的距离,而函数距离则决定了调用图中任意两个函数之间的距离。 更正式地说,我们将函数距离 df (n,n′) 定义为调用图 CG 中函数 n 和 n′之间沿最短路径的边数。 我们将函数 n 与目标函数 Tf 之间的函数级目标距离 df (n,Tf ) 定义为 n 与任意可达到的目标函数 tf∈ Tf 之间函数距离的调和平均值:

其中,R(n,Tf ) 是 CG 中 n 可到达的所有目标函数的集合。 调和平均数可以区分离一个目标更近、离另一个目标更远的节点,以及离两个目标距离相等的节点。 与此相反,算术平均值会将两个节点的目标距离分配为相同。 图 4 提供了一个示例。

图 4:以算术平均值定义的节点距离与调和平均值定义的节点距离之间的差异。 节点距离以白色圆圈表示。 目标用灰色标出。

    基本区块级目标距离决定了一个基本区块到所有其他调用函数的基本区块的距离,此外还有被调用函数的函数级目标距离的倍数。 直观地说,我们根据基本区块与调用链中其他调用函数的基本区块到目标位置的平均距离来分配目标距离。 此外,如果调用链较短,分配的目标距离就会较小。 BB 距离决定了 CFG 中任意两个基本区块之间的距离。 更正式地说,我们将 BB 距离 db (m1,m2) 定义为函数 i 的控制流图 Gi 中基本区块 m1 和 m2 之间沿最短路径的边的数量。假设 N (m) 是基本区块 m 调用的函数集合,使得 ∀n∈N (m). R(n,Tf )≠∅ .假设T 是 Gi 中基本块的集合,使得 ∀m ∈ T .N(m)≠∅ . 我们将基本区块 m 与目标基本区块 Tb 之间的基本区块级目标距离 db (m,Tb ) 定义为

其中c = 10是放大函数级距离的常数。注意,db (m,Tb)是对所有m∈Gi定义的。

    最后,我们有了定义归一化种子距离的所有要素,种子s到目标位置集合Tb的距离。设ξ(s)为一个种子s的执行轨迹。该轨迹包含执行过的基本块。我们定义种子距离d(s,Tb)为:

    模糊器持续保持一组种子S来模糊。我们将归一化种子距离d (s,Tb)定义为s到Tb的种子距离与之前任何种子s′∈S到Tb的最小种子距离之差除以最大种子距离之差。任意种子s′∈S到Tb的最小种子距离为:

注意,归一化种子距离d ∈[0,1]。还要注意,对于距离计算的重量级程序分析可以移到检测时进行,以便在运行时保持最小的性能开销。首先,提取调用图和程序内控制流图。这可以使用编译器本身来实现,也可以通过位码转换(或提升)来实现给定目标位置,可以在测量时计算函数级和基本块级目标距离。只有在运行时通过收集这些预先计算的距离值来计算规范化的种子距离

3.3 基于退火的能量调度:我们提出了一种新的基于退火的能量调度(APS)。Böhme等人[6]表明灰盒模糊可以被视为一个马尔可夫链,可以使用电力计划有效地导航这为我们提供了使用马尔可夫链蒙特卡罗(MCMC)优化技术的机会,例如模拟退火。我们基于退火的能量调度将更多的能量分配给离目标“更近”的种子,而不是“更远”的种子,这种能量差随着温度的降低而增加(即随着时间的推移)。

    模拟退火(SA)[19]的灵感来自冶金中的退火过程,这是一种涉及加热和控制材料冷却以增加其晶体尺寸并减少其缺陷的技术。类似地,SA算法向全局最优解集渐近收敛。在我们的例子中,这个集合,是执行最大数量目标位置的种子集合。SA是一种马尔可夫链蒙特卡罗方法(MCMC),用于在可接受的时间预算内在非常大的、通常是离散的搜索空间中逼近全局最优解。SA的主要特点是,在随机游走过程中,它总是接受较好的解,但有时也可能接受较差的解。温度是SA算法的一个参数,它调节较差解决方案的接受程度,并根据冷却计划降低。一开始,当T = T0 = 1时,SA算法可能会以高概率接受较差的解。最后,当T接近0时,它退化为经典的梯度下降算法,只接受更好的解。

图5 种子距离d (s,Tb)和时间对种子s能量p(s,Tb)的影响(tx = 40 min)

    冷却计划控制着收敛速度,它是初始温度T0 = 1和温度周期k∈N的函数。注意,能量对于一个种子是局部的,而温度对于所有种子是全局的。最常用的是指数冷却方案[19]:

其中α<1是一个常数,通常0.8≤α≤0.99。

    基于退火的能量调度。在自动化漏洞检测中,我们通常只有有限的时间预算。因此,我们想指定一个时间tx,退火过程在充分的“探索”时间后进入“开发”。直观地说,在时间tx,模拟退火过程可与经典的梯度下降算法(又名贪婪搜索)相媲美。我们让冷却计划在Tk≤0.05时进入开发阶段。对0.05以外的值和不同的冷却计划进行调整是直截了当的。因此,我们计算时刻t的温度Texp如下:

接下来,我们使用指数冷却计划来定义基于退火的能量调度(APS)。给定种子s和目标位置Tb, APS分配能量p为:

APS在当前时间t和归一化种子距离d三个值下的行为如图5所示。注意能量p∈[0,1]。而且,在开始搜索时(t = 0), APS分配给种子距离大的种子的能量与分配给种子距离小的种子的能量相同。随着时间的推移,只执行目标(即d = 0)的种子被分配越来越多的能量。

图6:基于退火的能量因子,控制最初由AFL的能量调度分配的能量(tx = 40), (a)与所有目标的最大距离(d  = 1)的种子和   (b)与所有目标的最小距离(d  = 0)的种子。注意y轴上的不同尺度。

    实用的集成。现在,AFL已经实施了一个能量调度。那么,我们如何集成APS呢?现有调度根据s的执行时间和输入大小、何时找到s以及s有多少祖先来分配能量。我们希望将AFL现有的能量调度与我们基于退火的能量调度整合起来,并定义最终的基于退火的综合能量调度。设Pafl(s)为AFL通常分配给种子s的能量。给定基本块Tb为目标,我们计算种子s 的积分为:APSp (s,Tb).

    基于退火的能量因子f = 210(p(s,Tb)-0.5)控制AFL能量调度分配的能量的增加或减少。对于归一化种子距离d (s,Tb)的两个极端情况,基于退火的能量因子的行为如图6所示。让我们考虑第一个极值情况,其中归一化种子距离最大(即d  (s,Tb) = 1;图6.)。在开始时(t = 0),能量因子f = 1,使种子分配的能量与AFL分配的能量相同(p (s,Tb) = pafl)。然而,仅仅十分钟后(t = 10min),同样的种子只分配了大约15%的原始能量。事实上,从式(12)和式(13)中我们可以看出

    换句话说,距离目标位置“非常远”的种子s被分配的能量越来越少,直到大约只有原始能量pafl的1 / 32被分配。现在让我们考虑第二个极值情况,其中归一化的种子距离是最小的(d  (s,Tb) = 1;图6. b)。在开始时(t = 0),能量因子f = 1,就像具有最大距离的种子一样。然而,由式(12)和式(13)可以看出

换句话说,一个“非常接近”到达目标位置的种子s被分配越来越多的能量,直到大约30倍于原始能量pafl被分配。

3.4 定向灰盒模糊测试的可扩展性:灰盒模糊测试的主要好处是它的效率很高,因为它不需要对任何程序进行分析;它在很短的时间内生成并执行大量的输入。现在,定向灰盒模糊测试(directed greybox fuzzing, DGF)似乎增加了一些程序分析,特别是控制流和调用图。那么DGF是如何扩展的呢?

    虽然我们的距离测量是程序间的,但程序分析实际上是程序内的。 这比程序间分析节省了大量成本。我们在程序间分析方面的经验如下。在很早的实例化过程中,AFLGo 会首先通过连接一个函数的所有调用点和被调用函数的第一个基本块来构建跨过程控制流图(iCFG)。 这已经花费了几个小时。 一旦 iCFG 可用,它将计算每个基本块在 iCFG 内的目标距离,即到达任何目标的最短路径的平均长度。 由于iCFG 中的节点数量庞大,这也需要几个小时的时间。 如今,AFLGo 完全跳过了 iCFG 计算在计算调用图中的函数级目标距离后,只计算基本模块级目标到同一程序内控制流图中调用站点的距离。在调用站点,函数级目标距离被用作通往目标的其余路径的近似值。 BB 级目标距离的核心是 Djikstra 的最短路径算法,其最坏情况下的复杂度为 O(V2),其中 V 是节点数。 假设有n个程序内 CFG,平均有m 个节点。 iCFG 中最短距离计算的复杂度为 O(n2*m2 )。 相比之下,我们在调用图和所有程序内控制流图中的最短距离计算复杂度为 O(n2+nm2)。这就节省了函数数量 n 的四次方。

    此外,DGF的设计使大多数重量级的分析可以转移到编译时(即插装时)。因此,DGF在运行时保持了大部分的效率。

    •编译时,在每个函数中为每个基本块计算BB级目标距离。对经典的AFL trampoline进行简单的扩展,将基本块目标距离添加到程序中的每个基本块。 trampoline是一组实现工具的指令。作为插装框架,LLVM可以非常高效地处理大型程序的静态分析。

    •在运行时,AFLGo与AFL一样可扩展,众所周知,AFL可以扩展到大型程序,如Firefox、PHP和Acrobat Reader。AFLGo trampoline只聚合基本块级目标距离值和执行的基本块数量,并且只比原始AFL trampoline包含少量的指令。基于退火的能量调度在模糊器本身实现。AFLGo没有理由不能像AFL那样规模化。

    总之,我们将大部分程序分析转移到插桩时间,以保持运行时的效率。在插桩过程中,通过轻量级程序内分析来计算程序间距离,从而保持程序分析的轻量级。根据我们的经验,这提供了巨大的节省,将测量时间从几个小时减少到几分钟。

   

图 7:架构: 图形提取器从源代码中生成调用图和控制流图后,距离计算器会计算每个基本块的基本块级目标距离,并在仪器化过程中供仪器使用。 仪器化的二进制代码不仅会告知模糊器覆盖范围,还会告知模糊器种子距离。

4 评估设置

    为了评估定向灰盒模糊测试的有效性和实用性,我们进行了一些实验。在现有的(无向)灰盒模糊器AFL[43]中实现了该技术,并将其命名为AFLGo。将AFLGo应用于两个不同的问题领域:补丁测试和崩溃重现,并将其与OSS-Fuzz[58]集成。

4.1 仪器化:

    AFLGo 由四个组件实现:图形提取器、距离计算器、仪表器和模糊器。 通过与 OSS-Fuzz 的集成,我们证明了这些组件可以无缝集成到原始构建环境(如 make 或 ninja)中。 整体架构如图 7 所示。 下面,我们将解释这些组件是如何实现的。

    (1) AFLGo图形提取器(GE)生成调用图(CG)和相关的控制流图(CFGs)。CG节点通过函数签名识别,CFG节点通过对应基本块的源文件和第一行语句识别。该GE是编译器AFL -clang-fast激活的AFL LLVM pass的扩展。将编译环境变量CC设置为afl-clang-fast并构建工程。

    (2) AFLGo距离计算器(DC)根据3.2节将调用图和每个过程内控制流计算每个基本块(BB)的过程间距离。DC是用Python脚本实现的,它使用networkx包来解析图形,并根据Djikstra算法进行最短距离计算。DC生成BB-distance文件,其中包含每个BB的基本块级目标距离。

    (3) AFLGo仪器获取BB-distance文件并将每个BB记录到目标二进制文件中。具体来说,对于每个BB,确定各自的BB级目标距离并注入扩展的trampoline。trampoline是一段汇编代码,在每个跳转指令之后执行,以跟踪覆盖的控制流边缘。在64kb共享内存中,边由字节标识。在64位体系结构上,我们的扩展使用了额外的16字节共享内存:8字节用于累积距离值,8字节用于记录执行的BBs数量。对于每个BB, AFLGo仪器添加汇编代码 i)加载当前累积距离并添加当前BB的目标距离,ii)加载并增加所执行的BBs数量,iii)将这两个值存储到共享内存中。该插桩是作为AFL LLVM通道的扩展实现的。编译器设置为afl-clang-fast,编译器标志引用BB-distance文件,使用ASAN[35]构建项目。

    (4) AFLGo模糊器被实现到AFL 2.40b版本中(该版本已经集成了AFLFast的探索计划[6])。它根据我们基于退火的能量调度(见第3.3节)模糊测量二进制。共享内存中额外的16字节告诉模糊测试器当前的种子距离。将累积的BB距离除以实际执行的BBs数量,计算出当前种子距离。

4.2 基础设施:我们在使用Intel Xeon CPU E5-2620v3处理器的机器上执行所有实验,该处理器有24个逻辑核心,运行在2.4GhZ,访问64GB主内存,操作系统为Ubuntu 14.04(64位)。我们总是正好使用22个内核来保持工作负载的可比性,并为其他进程保留两个内核。

    对于AFLGo与基线(即AFL, Katch或BugRedux)的所有实验比较,两个模糊器都是从相同的种子语料库开始的。如果没有可用的种子语料库,我们将空文件作为种子语料库启动AFLGo(即echo “” >in/file)。对于所有的实验比较,两个模糊测试器都具有相同的时间预算和计算资源,以到达相同的目标位置集。

5.应用1:补丁测试

    本文展示了定向灰盒模糊测试在补丁测试中的应用,并将实现AFLGo与最先进的补丁测试工具Katch进行了比较。假设像Libbfd这样的安全关键库正在持续地进行模糊测试,并且很长一段时间内都没有发现漏洞。现在,库进行了更改,添加了一个新功能。在接下来的模糊测试活动中,专注于这些变化,检查最近的变化是否引入了新的漏洞,是有好处的。目前最先进的补丁测试工具是Katch[21],这是一个基于符号执行引擎Klee[7]的定向白盒模糊器。

图8:基准Katch的描述

    在实验中,我们将定向灰盒模糊器AFLGo和Katch在补丁覆盖率和漏洞检测方面进行了比较。我们使用与Katch作者相同的主题、实验参数和基础架构。但是,我们排除了最小的主题findutils,因为它具有执行任意命令和删除任意文件的能力。AFLGo实际上执行插装的二进制文件,而Katch只是解释LLVM字节码。图8显示了其他主题的一些描述性统计。虚拟基础设施是根据Katch作者的要求提供的。我们在基础设施中重用相同的脚本来确定目标位置并分析结果。我们使用相同的种子语料库并设置相同的超时时间(即,Diffutils为每个目标10分钟,Binutils为每个目标15分钟)。保守地说,我们只给AFLGo提供了一个虚拟核和大约3GB的主内存,而给Katch提供了四个核和16GB的主内存。

    不过,我们要指出的是,在进行此类工具比较时,一定要慎重。 经验评估始终只是比较两个概念的实现,而不是概念本身。 提高效率或扩展搜索空间可能只是一个与概念无关的 "工程努力 "问题[32]。 我们有意识地努力解释观察到的现象,并区分概念与技术的渊源。 此外,我们鼓励读者从安全研究人员的角度来考虑问题,因为他们实际上是在处理这些工具,以确定是否存在漏洞。

5.1 补丁覆盖率

    我们首先分析 Katch 和 AFLGo 实现的补丁覆盖率,衡量标准是在各自补丁中改变的先前未覆盖的基本区块的数量。

表1  补丁覆盖率,分别显示了Katch和AFLGo在规定的时间内能够覆盖的先前未覆盖目标的数量。

图9  维恩图显示了Katch 和AFLGo分别覆盖和共同覆盖的已更改过的BBs的数量。

    AFLGo比Katch多覆盖了13%以前未发现的更改的基本块。AFLGo覆盖了223个之前未发现的改变的基本块,而Katch覆盖了198个。表1的列2显示了改变的基本块的总数,而列3显示了那些还没有被现有的回归测试套件覆盖的。最后,第4列和第5列分别显示了Katch和AFLGo覆盖的先前未覆盖的基本块的数量。我们将之前未发现的更改基本块称为目标。

虽然我们预计 Klee 会大幅领先,但在与 Katch 论文一起发表的同一基准测试中,AFLGo 的补丁覆盖率实际上超过了 Katch。

    我们分析了剩余目标未被覆盖的原因。仍存在许多未覆盖的基本快,因为它们存在于不可访问的代码中,例如,只有在某些操作系统上才能访问。MacOS、Windows)或某些体系结构(例如ARM或MIPS)。另一些则是由于我们与Katch共享的当前原型的限制。例如,超过一半的基本块只能通过寄存器间接调用或跳转(例如,从函数指针)访问。在已分析的调用图或控制流图中,它们不会作为边出现。此外,当程序需要复杂的输入结构时,符号执行和灰盒模糊测试都会因庞大的搜索空间而陷入困境。例如,许多Binutils目标只有在种子文件包含特定的节,并具有单独定义的结构时才能执行。这两种技术都将受益于更高质量的回归测试套件和基于模型的模糊测试方法[28]。

    为了了解研究人员如何从两种方法中获益,我们调查了两种技术所覆盖的目标集。 如图 9 所示,AFLGo 可以覆盖 Katch 无法覆盖的 84 个目标,而 Katch 则可以覆盖 AFLGo 无法覆盖的 59 个目标。 我们将合理的小交集归因于每种技术的各自优势。 符号执行可以解决困难的约束条件,从而进入原本难以进入的 "区域"[38]。 另一方面,灰盒模糊器可以快速探索通往目标的多种路径,而不会陷入特定的路径 "邻域"。

虽然我们预计 Klee 会大幅领先,但在与 Katch 论文一起发表的同一基准测试中,AFLGo 的补丁覆盖率实际上超过了 Katch。

    AFLGo和Katch是互补的。他们共同覆盖了282个目标,比Katch和AFLGo各自能覆盖的目标分别多42%和26%。

    作为未来的工作,我们计划整合基于符号执行的定向白盒模糊测试和定向灰盒模糊测试,以实现一种既高效又能到达预定目标位置的定向模糊测试技术。我们相信这样的集成将优于单独的每种技术。综合利用符号执行和搜索作为优化问题的定向模糊测试技术,能够利用它们的组合优势来缓解各自的弱点。Driller[38]是一个集成了AFL灰盒模糊器和Klee白盒模糊器的(无向)模糊器的例子。

5.2 漏洞检测

表2:显示AFLGo发现的以前未报告的bug数量(报告于2017年4月)以及Katch的bug报告数量(报告于2013年2月)。

    AFLGo发现了13个以前未报告的bug,以及Katch发现的7个bug中的4个。尽管Katch对补丁进行了彻底的测试,AFLGo还是暴露了在最新版本中仍然存在的13个bug.这些bug主要由缓冲区溢出和空指针解引用组成;Katch(和Klee)通常可以处理的错误类型。之前未报告的bug中有7个存在于libbfd-library中,它们被分配为CVEIDs。Libbfd允许操作为各种指令集体系结构编译的目标文件。Libbfd广泛应用于汇编器、链接器和二进制分析工具中。

所提出的定向灰盒模糊器在漏洞检测方面明显优于目前最先进的技术。AFLGo是一种有效的补丁测试工具。

    我们研究了这些错误的发现与目标位置的关系。 AFLGo 的定向性直接导致发现了 12 个错误。 具体来说,7 个错误的堆栈跟踪包含了目标位置;另外 5 个错误是在目标位置附近发现的。 最初的 Katch 实验是在四年多前(13 年 2 月)对 binutils 和 diffutils 的 356 次提交进行的。 自早期的错误报告以来,又有无数其他错误被报告。 我们报告的 13 个 bug 在最新版本的 binutils 和 diffutils 中仍然存在(见表 3)。但是,我们认为 AFLGo 在实验对象中暴露了更多的错误,这些错误后来都被修复了。

表3 AFLGo发现的Bug报告和CVE-IDs。

    我们将 AFLGo 相对于 Katch 的优势主要归功于定向灰盒模糊处理的效率。 AFLGo 不需要在运行时分析程序,因此生成的输入比 Katch 多几个数量级。 另一个效率来源是运行时检查,而 Katch 需要基于约束的错误检测机制。 当运行时检查器[14, 35, 37]检测到程序执行错误时,程序就会崩溃。 例如,我们在实验对象中安装了运行时检查器 AddressSanitizer (ASAN)。 如果输入导致非法内存读取或写入,例如,读取的内存超出了为缓冲区分配的内存,或者写入的内存已经被释放,那么即使程序通常不会崩溃,ASAN 也会发出 SEGFAULT 信号。 模糊器利用运行时检查器发出的这一信号,为生成的输入报告错误。相比之下,Katch 是以符号方式执行(即解释)程序的 LLVM 中间表示,并使用约束求解来确定是否存在错误。 大多数基于符号执行的白盒模糊器将错误检测直接集成到约束求解过程中。 这种嵌入将检测限制在可以编码为违反约束的错误上。 作为符号解释器基础的环境模型的不完整性进一步影响了错误检测。例如,我们在 diffutils 中发现的错误会在 GLIBC 的正则表达式组件中导致取消引用空指针。 然而,Katch 实现了一个名为 Klee-uCLIBC 的 GLIBC 简化模型,因此不可能在 diffutils 中发现这个 bug。 相比之下,作为灰盒模糊器的 AFLGo 会具体执行编译后的二进制文件,并报告任何崩溃输入。

6应用2:持续测试

    为了研究定向灰盒模糊测试的实用性,我们将AFLGo集成到谷歌发布的oss - fuzz中(2016年12月)。OSS-Fuzz[58]是一个针对安全关键库和其他开源项目的持续测试平台。OSSFuzz以完全自动化的方式定期检查注册的项目、构建并进行模糊测试。Bug报告会自动提交给项目维护者,并在Bug被修复后关闭。在新项目的启动过程中,维护者提供构建脚本并为OSS-Fuzz实现一个或多个测试驱动程序。截至17年5月,已集成47个开源项目。在谷歌的机器上,OSS-Fuzz每天处理10万亿(1013)个输入,并发现了1000多个bug,其中264个是安全关键漏洞[59]。不过,虽然 OSS-Fuzz 总是对最新版本进行模糊测试,但模糊器(AFL [43] / LibFuzzer [53])并不针对可能引入新漏洞的最新更改。

表4测试项目和AFLGo的发现。

    我们将AFLGo集成到谷歌的全自动模糊测试平台OSS-Fuzz中,在7个安全关键开源项目中发现了26个不同的bug(见表4)。在被测试的5个项目中,有一个漏洞可以用于远程漏洞攻击,因为它们是面向互联网的。我们评估了10个安全关键漏洞,它们可以被利用进行拒绝服务攻击或潜在的远程任意代码执行攻击。

对于OSS-Fuzz来说,AFLGo是一个成功的补丁测试工具,即使在模糊测试良好的项目中也可以发现漏洞。

    在下文中,我们将研究 AFLGo 的发现,以及它的指向性如何有助于暴露这些错误。 我们特别关注在 LibXML2 和 LibMing 中的发现。

6.1  LibXML2

    LibXML2 [55] 是一个广泛使用的C语言XML解析库,也是PHP的核心组件之一。通过对LibXML2最近的50次提交进行模糊测试,AFLGo发现了四个(4)不同的缓冲区溢出崩溃问题。所有这些问题都可以在最新版本PHP的DOM验证器中重现。其中两个问题是无效写入,大小可达4kb,可能被利用来执行任意代码。我们提交了错误报告、补丁,并发布了安全公告。以下是分配的四个CVE编号:CVE-2017-{9047,9048,9049,9050}

    我们确定了两个导致崩溃的问题(CVE-2017-{9049,9050})是修复不完全的结果。所谓修复不完全的漏洞补丁,是指试图修复先前报告的漏洞,但该漏洞仍然可以复现。漏洞可能在原始漏洞报告中的输入上得到修复,但其他输入仍然可以暴露该漏洞。在这种情况下,第一个漏洞在“非冒号化”名称(NCNames)的解析器中得到修复,但在更通用的名称(Names)解析器中未修复。第二个漏洞在 HTML 解析器(htmlParseName)中得到修复,但在通用的 XML 解析器(xmlParseName)中未修复。通过将灰盒模糊测试工具指向这些漏洞先前修复中修改的语句,AFLGo 有效地生成了其他会暴露这些本应已修复的漏洞的崩溃输入。

    其他导致崩溃的问题(CVE-2017-{9047,9048})集中在同一个提交附近。在提交 ef709ce2 中,开发者通过在 valid.c 文件的 xmlAddID 方法中添加边界检查来修补空指针解引用的问题。这个函数由另外两个函数调用,分别是 xmlParseOneAttribute 和 xmlValidateOneNamespace。然而,如图10所示,为了到达这些函数,解析器必须首先执行 xmlValidateOneElement。当这个函数在不同点打印元素内容时,两个以前未报告的溢出问题就会暴露出来。

图10:包含 CVE-2017-{9047,9048} 的函数(加粗显示)位于通过那些灰色背景显示的路径上的函数,这些函数在提交 ef709ce2 中被更改。

AFLGo能够发现先前报告过但本应修复的漏洞。此外,AFLGo还能够在容易出错的软件组件中发现新的漏洞,这些组件往往会被频繁修补。。

6.2 LibMing

    Libming [56] 是一个广泛使用的库,用于读取和生成 Macromedia Flash 文件(.swf),在 PHP 版本 5.3.0 之前曾捆绑在一起,现在作为扩展可用。通过对 LibMing 最近的 50 个提交进行模糊测试,AFLGo 发现了一个修复不完全的问题。这个漏洞最近由另一位安全研究人员发现,获得了 CVE-2016-9831,并由维护者发布了更新 libming 的安全公告进行修补。然而,针对这些最近的更改,AFLGo能够生成另一个导致崩溃的输入,产生完全相同的堆栈跟踪(见图11)。这个补丁是不完全的。我们提交了一个详细分析和补丁的漏洞报告,经过审查并被接受。CVE-2017-7578 被分配用于标识这个漏洞。

图11:这个先前报告的漏洞曾经被修复过,但修复不完全。AFLGo能够在最新(修复过的)版本上复现完全相同的堆栈跟踪。

7 应用3:漏洞复现

    Directed greybox fuzzing也适用于崩溃复现。许多生产知名商业软件的公司都拥有自动化的漏洞报告设施。例如,当VideoLAN客户端(VLC)[64]由于LibPNG [54]中的缓冲区溢出而崩溃时,用户可以通过按下按钮选择向开发者报告此漏洞。这些自动化的漏洞报告对公司来说不仅有助于确保服务质量,从安全的角度来看,缓冲区溢出是一种可以被利用以获取任何用户机器的根访问权限的漏洞。然而,VideoLAN组织考虑到用户的隐私,不允许发送导致视频播放器崩溃的特定文件。毕竟,这些文件可能包含机密信息。相反,软件会发送堆栈跟踪和一些环境参数。最终由内部开发团队负责复现可能具有安全风险的漏洞。

    本文通过比较AFLGo和基线AFL [43]的效率来评估 directed greybox fuzzing 是否确实是有方向性的。此外,我们通过将AFLGo与BugRedux进行比较,来评估应用于崩溃复现的 directed greybox fuzzing 是否优于当前崩溃复现领域的最新技术。BugRedux [18] 是基于Klee [7]的一种基于定向符号执行的白盒模糊测试工具,它接受程序崩溃堆栈跟踪中的目标位置序列(例如方法调用),并生成一个输入,以按照给定顺序执行这些目标位置,目的是导致程序崩溃。

7.1 定向灰盒测试真的是定向的吗?

    在这个实验中,我们使用我们的定向灰盒模糊测试工具 AFLGo 来评估内部开发团队通过指定堆栈跟踪中的方法调用作为目标位置,自动复现崩溃的速度。我们将平均复现崩溃所需的时间与无向灰盒模糊测试工具 AFL 进行比较。我们选择的实验对象如图12所示。

为了将 AFLGo 与基线 AFL 进行比较,我们选取了在我们先前研究中报告的 Binutils 中的漏洞。然而,为了减少任何潜在的实验者偏差,我们从最近报告的 LibPNG [54] 的十大可复现漏洞中选择了漏洞。Binutils 是一个二进制分析工具集,包含近百万行代码(LoC),而 LibPNG 是一个图像库,包含近50万行代码(LoC)。这两者都是广泛使用的开源 C 项目。漏洞通过其 CVE-ID 标识,并在美国国家漏洞数据库 [63] 中有记录。每次实验设定了八小时的超时限制和七小时的开发时间(tx)阈值。我们重复这个实验20次,以获取可靠的数据进行分析。

图12 复现漏洞类型

    我们使用以下指标来衡量模糊测试效率和性能提升。暴露时间(TTE)衡量模糊测试活动直到生成第一个暴露特定错误的测试输入的时间长度。我们通过在一组修复版本上执行失败的输入来确定测试用例暴露了哪个错误,其中每个版本仅修复一个错误。如果输入在修复版本上通过测试,则认为其见证了相应的错误。如果这是第一个此类测试用例,则认为其暴露了错误。改进因子(Factor)衡量性能提升,将AFL的平均TTE除以AFLGo的平均TTE。Factor值大于1表示AFLGo优于AFL。Vargha-Delaney统计量A 12)是一种非参数效应量度量方法【39】,也是评估随机算法的推荐标准度量方法【1】。给定在m次AFLGo测量和n次AFL测量中观察到的性能度量M(如TTE),A 12统计量衡量运行AFLGo比运行AFL产生更高M值的概率。我们使用Mann-Whitney U检验来衡量性能提升的统计显著性。当显著时,我们将A 12值标为粗体。

表5 AFLGo对AFL的性能。我们运行这个实验20次,并以粗体突出显示统计显著值A 12。如果在8小时内没有重现该漏洞,则会收到8小时的TTE。

    LibPNG为了在LibPNG中重现cve, AFLGo比AFL快3到11倍。更多细节如表5所示。在8小时内,我们可以生成一个崩溃的输入,以收集LibPNG中3个cve的堆栈跟踪。对于CVE-2015-8540, AFLGo只需要几秒钟就可以重现漏洞,而AFL则需要近5分钟。对于CVE-2011-3328, AFLGo只需半个小时,而AFL需要三个小时。对于剩余的CVE, AFLGo可以在4分钟内重现漏洞,而AFL需要4倍多的时间。

作为AFL的扩展,AFLGo是一个有效的定向。与AFL不同,它可以成功地指向提供的目标位置。此外,AFLGo是一个有效的漏洞重现工具。

    Binutils为了在Binutils中重现cve, AFLGo通常比AFL快1.5到2倍。两个cve,很难暴露。AFL和AFLGo都只能在不到35%的运行中重现这些漏洞,因此差异不具有统计学意义。表5左侧的4个cve通常在10分钟内重现,AFLGo可以获得比AFL高达3.5倍的加速。只有在CVE-2016-4490上,AFLGo表现出了与AFL相同的性能。然而,CVE在几秒钟内就会暴露出来,这种规模的外部影响是不可忽视的。

7.2 DGF的性能是否优于基于符号执行的最新技术?

    在本实验中,我们将AFLGo与BugRedux进行比较,BugRedux是目前崩溃重现领域的最先进技术。我们使用相同的数据集以及相同的实验设置、起始配置和超时时间。BugRedux【18】是一种基于Klee的定向白盒模糊测试工具,它以程序语句序列作为输入,并生成一个测试用例来执行该序列并导致程序崩溃。研究表明,当提供导致崩溃的完整方法调用序列时,BugRedux表现最佳。然而,如前所述,通常只提供堆栈跟踪,而堆栈跟踪并不包含已经“返回”(即已完成执行)的方法。因此,在我们的比较中,我们将堆栈跟踪中的方法调用设为目标。尽管我们请求了原始数据集中的所有对象,但最终只能找到其中的九个对象。对于两个对象(exim, xmail),我们无法获得指定目标位置的堆栈跟踪。具体而言,exim的崩溃只能在32位架构上重现,而xmail的崩溃会导致堆栈溢出,从而覆盖堆栈跟踪。其余七个对象的结果如表6所示。

表6 BugRedux原始对象的bug重现

    AFLGo在其自身基准上明显比BugRedux更有效。仅仅有一个崩溃是AFLGo无法重现的,这是由于一个简单的工程问题,即AFLGo无法生成多个文件。AFLGo在不到十分钟内(< 10分钟)重现了六个崩溃中的四个,而剩下的两个(gzip.1+2)则在大约四小时内(≈ 4小时)重现,远低于24小时的时间预算。

    仅给定堆栈跟踪,AFLGo可以比BugRedux多再现3倍的崩溃。

8 有效性威胁

    第一个关注点是外部有效性,特别是通用性。首先,我们的结果可能不适用于未测试的对象。然而,我们对各种开源C项目进行了实验,这些项目包含了最多具有安全关键漏洞的软件类别。当将这一类别的程序发布的CVE数量与任何其他类别的程序发布的CVE数量进行比较时,可以确定这确实覆盖了最大的类别【57】。其次,与除Katch或BugRedux之外的定向白盒模糊测试工具进行比较可能会得出不同的结果。然而,Katch和BugRedux是基于Klee的最先进的定向模糊测试工具。Klee【7】是最广泛使用的符号执行引擎,是大多数针对C的定向白盒模糊测试工具的基础【15, 16, 33】。Katch由Klee的作者实现。此外,我们确保与Katch和BugRedux的比较是公平的:我们重用了作者在原始论文中用来展示有效性的相同基准【18, 21】。

    第二个关注点是内部有效性,即研究在多大程度上减少系统误差。首先,对于模糊测试实验来说,常见的内部有效性威胁是初始种子的选择。然而,在我们的实验中,我们始终使用现成的语料库,例如Binutils的现有回归测试套件和Katch实验,OSS-Fuzz实验的可用语料库,以及AFL为最重要的文件格式经典提供的种子语料库。此外,在比较两个模糊测试工具时,它们总是以相同的种子语料库开始,这样两个模糊测试工具都能获得相同的(劣)优势。其次,像其他技术的实现一样,AFLGo可能无法忠实地实现此处介绍的技术。然而,正如与AFL的比较所示,AFLGo是有效定向的。

    第三个关注点是构建效度,即测试是否真正衡量其所声称或假定的内容。我们注意到,工具比较的结果应当谨慎对待。经验评估总是只比较两个概念的实现,而不是概念本身。提高模糊测试工具的效率或扩展其搜索空间可能只是一个“工程努力”的问题,与概念无关【32】。然而,我们有意识地努力解释观察到的现象,并区分概念性和技术性起源。此外,我们鼓励读者从实际处理这些工具的安全研究人员的角度来考虑,以确定是否存在漏洞。

9 相关工作

    我们首先调查现有的定向模糊测试方法及其典型应用。接下来,我们调查基于覆盖率的模糊测试,其目标是生成能够实现最大代码覆盖率的输入。最后,我们讨论基于污点分析的定向模糊测试工具,其目标是识别并模糊处理种子中特定的输入字节,以在给定程序位置达到特定值。

    据我们所知,定向模糊测试主要是通过符号执行引擎实现的,例如Klee【7】。**定向符号执行(DSE)**利用符号执行、程序分析和约束求解的强大工具系统地且有效地探索可行路径的状态空间【20】。一旦识别出一条可以实际到达目标的可行路径,就会生成测试用例作为相应路径约束的解决方案。DSE已被用于到达危险的程序位置,例如关键系统调用【15】、覆盖补丁中的变化【3, 21, 34】、覆盖之前未覆盖的程序元素以增加覆盖率【66】、验证静态分析报告【9】、进行变异测试【16】以及在内部重现现场故障【18, 33】。与DSE相比,**定向灰盒模糊测试(DGF)**不需要符号执行、程序分析和约束求解的复杂工具。DGF实现的轻量级程序分析完全在编译时进行。我们的实验表明,DGF可以优于定向白盒模糊测试(DWF),而且这两种技术结合在一起比单独使用任何一种技术都更有效。

    基于覆盖率的模糊测试旨在通过某种方式增加种子语料库的代码覆盖率。希望一个不执行程序元素e的种子语料库也无法发现e中可观察到的漏洞。基于覆盖率的灰盒模糊测试工具【6, 31, 36, 43, 53】使用轻量级的插桩技术在运行时收集覆盖率信息。有几种增强技术。AFLFast【6】将模糊测试活动集中在低频路径上,以在单位时间内执行更多路径。Vuzzer【31】给某些基本块(例如错误处理代码)分配权重,以优先处理更可能揭示漏洞的路径。Sparks等人【36】使用遗传算法来演化固定大小的种子语料库和输入语法,以深入控制流逻辑。基于覆盖率的白盒模糊测试工具【7, 12, 13】使用符号执行来增加覆盖率。例如,Klee【7】有一种搜索策略,优先处理更接近未覆盖基本块的路径。这两种方法的结合和集成也进行了探索【26, 38】。与定向灰盒模糊测试相比,基于覆盖率的模糊测试工具将所有程序元素视为目标,以实现最大代码覆盖率。然而,如果目标只是到达特定的一组目标位置,强调不相关的组件会浪费资源。

    基于污点分析的定向白盒模糊测试利用经典的污点分析【17, 25】来识别种子输入中应优先进行模糊处理的特定部分,以增加生成所需数值的概率,从而观察目标位置的漏洞(例如,在除法运算符的分母中生成零值)【11, 31, 40】。这可以显著减少搜索空间。例如,Buzzfuzz【11】标记种子文件的部分作为可模糊处理的部分,这些部分控制所有执行的关键系统调用的参数。

    大部分种子文件并不需要进行模糊处理。基于覆盖率的模糊测试工具Vuzzer【31】利用污点分析来执行通常难以到达的代码。在识别相关的条件语句后,Vuzzer利用污点分析以增加达到不同分支结果的概率。与DSE不同,基于污点的定向白盒模糊测试不需要符号执行和约束求解的复杂工具。然而,它要求用户提供一个能够已经到达目标位置的种子输入。相比之下,AFLGo甚至可以从一个空的种子输入开始,这在我们的实验中显而易见。在未来的工作中,研究我们的定向灰盒模糊测试如何从Vuzzer中实现的类似基于污点的方法中受益将会很有意义:在运行时,分析首先识别那些需要否定以减少距离的条件语句,然后使用污点分析增加在模糊测试期间实际否定这些语句的概率。然而,我们的直觉认为,灰盒模糊测试成为漏洞检测的最先进技术的主要贡献因素是其效率;能够每秒生成和执行数千个输入。秉持这一理念,我们设计了DGF,在编译时进行所有重型分析,同时在运行时保持其效率。

10 总结

    基于覆盖率的灰盒模糊测试工具如AFL尝试覆盖更多的程序路径,而无需进行程序分析的任何额外开销。基于符号执行的白盒模糊测试工具如Klee或Katch使用符号程序分析和约束求解,在需要时精确地指导测试生成的搜索过程。

    符号执行一直是实现定向模糊测试的首选技术【3, 4, 9, 15, 20, 21, 27, 29, 33, 34, 66】。达到给定目标只是解决正确路径约束的问题。符号执行提供了一个分析性的、数学严谨的框架,专门用于探索路径以达到目标位置。相比之下,灰盒模糊测试本质上是一种随机方法,不直接支持定向性。基本上,任何灰盒模糊测试工具只是对随机种子文件的随机位置应用随机变异。

    在本文中,我们尝试将定向性引入灰盒模糊测试中。为了在运行时保持灰盒模糊测试的效率,我们将大部分(轻量级)程序分析移到插桩时间,并在测试生成过程中使用模拟退火作为实际的全局元启发式算法。与定向符号执行不同,定向灰盒模糊测试不会因为重型程序分析、执行指令的路径约束编码和求解而产生运行时性能开销。

    我们的定向灰盒模糊测试工具AFLGo只实现了几千行代码,易于设置。事实上,将其集成到OSS-Fuzz中使AFLGo能够覆盖超过50个不同的安全关键程序和库。与符号执行不同,定向灰盒模糊测试工具本质上是随机的,无法系统地引导其朝着给定的一组目标前进。因此,在我们的实验中,令人惊讶的是,AFLGo在效果上(即,AFLGo检测到更多漏洞)和效率上(即,AFLGo在相同时间内达到更多目标)都表现得比现有的基于定向符号执行的白盒模糊测试工具(如BugRedux和Katch)更好。

    定向灰盒模糊测试可以在多种方式下使用:用于引导搜索到有问题的变更或补丁、到关键系统调用或危险位置,或者到报告的漏洞堆栈跟踪中希望重现的函数。我们展示了定向灰盒模糊测试在补丁测试(需要到达补丁代码中的位置)和现场失败的崩溃重现(需要重现堆栈跟踪)中的应用。我们还讨论了将我们的定向模糊测试工具集成到持续模糊测试平台OSS-Fuzz中的情况。

    作为未来的工作,我们计划将基于符号执行的定向白盒模糊测试和定向灰盒模糊测试进行集成。一个整合的定向模糊测试技术,结合了符号执行和搜索优化问题,能够充分发挥它们的优势,以减轻各自的弱点。正如我们实验所显示的那样,这将导致更加有效的补丁测试,以揭示潜在的易受攻击的程序更改。我们还计划评估将AFLGo与静态分析工具集成时的效果,该工具能够指出危险位置或安全关键组件。这将使我们能够将模糊测试的精力集中在更可能包含漏洞的边缘情况上。

    为了下载我们的工具AFLGo以及我们与OSS-Fuzz的集成,读者可以执行以下命令

$ git clone https://github.com/aflgo/aflgo.git

$ git clone https://github.com/aflgo/oss-fuzz.git

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值