FuzzFactory: Domain-Specific Fuzzing with Waypoints

参考https://www.cnblogs.com/fuhara/p/13246485.html
https://github.com/rohanpadhye/FuzzFactory
FuzzFactory在模糊算法和测试程序之间暴露了一个API(见include/waypoints.h)。测试程序可以以键值对的形式提供来自测试执行的特定领域的反馈,并通过选择一个还原函数来指定这种反馈应该如何在多个输入中进行聚合。汇总后的反馈被用来决定一个给定的输入是否应该被视为一个航点。对API的调用可以通过手工修改测试程序,或在编译时在测试程序中插入适当的工具来注入。

摘要

覆盖率引导的模糊测试作为一种在解析二进制数据的程序中发现安全漏洞(如缓冲区溢出)的高效方法,已经得到了重视。最近,研究人员针对不同的领域特定测试目标,对覆盖率引导的模糊算法进行了各种专门化,如发现性能瓶颈、生成有效输入、处理魔法字节比较,每一个这样的解决方案都可能需要非平凡的实现工作,并产生一个独特的模糊工具变体。我们观察到,许多特定于领域的解决方案遵循一种常见的解决方案模式。在本文中,我们提出了FuzzFactory,一个开发特定领域模糊应用程序的框架,无需更改变异和搜索启发式。FuzzFactory允许用户在测试执行期间指定特定于域的动态反馈的集合,以及如何聚合此类反馈。FuzzFactory使用此信息有选择地保存中间输入(称为航路点),以增强覆盖率引导的模糊化。这些航路点总是朝着特定领域的多维目标前进。我们使用FuzzFactory实例化了六个特定于领域的模糊化应用程序:三个以前工作的重新实现和三个新的解决方案,并在Google的fuzzer测试套件的基准上评估了它们的有效性。我们还展示了如何组合多个域,使其性能优于各部分的总和。例如,我们结合了关于严格相等比较和动态内存分配的特定于域的反馈,以实现LZ4炸弹和PNG炸弹的自动生成。

介绍

最近的工作表明,模糊测试的应用不仅仅局限于发现程序崩溃。例如,模糊测试可用于定向测试[Böhme et al.2017],基于属性的测试[Padhye et al.2019a],差异测试[Petsios et al.2017a],侧通道分析[Nilizadeh et al.2019],发现算法复杂性漏洞[Petsios et al.2017b],发现性能热点[Lemieux et al.2018]等。在每种情况下,研究人员都修改了原始模糊算法以产生专门的解决方案。类似地,研究人员调整了原始CGF算法,以利用程序中的特定领域信息,以提高代码覆盖率,例如在文件格式中使用魔法字节[LafIntel2016;Li等人2017;Rawat等人2017]或输入有效性测量[Laeufer等人2018;Padhye等人2019c;Pham等人2018]。目前,开发特定领域的模糊应用程序的实践是非常特别的。对于每个新领域,研究人员必须找到一种方法来调整模糊算法,并产生AFL或其他模糊工具的新变体。每个这样的解决方案都可能需要非平凡的实现。此外,这些变体是独立的不容易合成。
在本文中,我们提出了FuzzFactory,一个用于实现特定领域模糊应用程序的框架。我们的框架基于以下观察:许多领域特定的模糊问题可以通过增加覆盖率引导的模糊算法来解决,以选择性地保存新生成的输入,用于后续的变异,而不仅仅是那些只提高代码覆盖率的问题。我们称这些中间输入为航路点,其灵感来自于导航领域的相应术语。这些航路点给出了实现特定领域目标的模糊算法步骤。域d的特定于域的模糊应用程序通过谓词指定:is_waypoint(i,S,d)。这个谓词回答了以下问题:给定一个新生成的输入和一组先前保存的输入,我们是否应该将输入i保存到S?FuzzFactory提供了一种简单的机制,用于定义is_waypoint,该机制基于在测试执行期间可以动态收集的特定于域的反馈。特定于领域的模糊化应用程序可以通过FuzzFactory提供的一小组API对测试中的程序进行测试,以收集此类定制反馈。
FuzzFactory支持开发特定于领域的模糊应用程序,而无需更改底层搜索算法。我们能够轻松地重新实现先前工作中的三种算法,并评估它们的优缺点:SlowFuzz[Petsios等人2017b]、PerfFuzz[Lemieux等人2018]和validity fuzzing[Padhye等人2019c]。我们还使用dfuzzfactory原型化了三个新的应用程序:用于平滑硬比较,用于生成分配过多内存的输入,以及在代码更改后执行增量模糊化。我们描述了这六个特定于领域的模糊应用程序,以及我们在谷歌[2019b]发布的测试套件中的六个真实基准测试程序上的实验结果。Fuzzfactory的一个关键优势是特定领域的反馈是自然可组合的。我们将特定于领域的模糊应用程序结合起来,以加剧内存分配并平滑硬比较,从而生成一个性能优于其每个组成部分的复合应用程序。复合应用程序自动生成LZ4炸弹和PNG炸弹:微小的输入分别导致动态分配4GB和2GB的IBPNG炸弹。
综上所述,我们在本文中做出了以下贡献:
(1) 我们介绍了fuzzfactory,这是一个使用在测试执行期间动态收集的自定义反馈来指定特定于域的模糊应用程序的框架。
(2) 我们描述了一种特定于领域的模糊算法,该算法结合了自定义反馈以及用户提供的减缩函数,以选择性地保存中间输入,称为航路点。
(3) 我们确定了reducer函数必须满足的关键属性,以确保每个保存的航路点都有助于特定领域的进展。
(4) 我们描述了使用我们的框架实现的六个特定于领域的模糊应用程序的实现,以及我们在六个实际测试程序上对这些应用程序的实验评估结果。
(5) 我们描述了如何组合多个特定于领域的模糊应用程序,并以经验的方式展示了这种组合如何比它们的组成部分表现得更好。
(6) 我们描述了特定于领域的模糊化框架FuzzFactory提供的API,并公开了该工具athttps://github. com/rohanpadhye/fuzzfactory。
在这里插入图片描述

动机

在这里插入图片描述

考虑图1a中的示例测试程序,函数测试采用两个16位整数a和b作为输入。一个常见的测试目标是生成在这个程序中最大化代码覆盖率的输入。我们使用算法1对这个测试程序执行CGF。假设我们从种子输入开始:a=0x0000,b=0x0000。种子输入不满足第2行的条件。CGF算法随机地对这个种子输入进行变异,并在变异的输入上执行测试程序,同时寻找新的代码覆盖率。图1b在灰色框中描述了CGF可以保存的一系列示例输入,从黄色框中的初始种子输入i1开始。两个输入之间的一个实心箭头,比如i和i′,表示输入i被突变以生成i′。经过一些尝试后,CGF可能会将i1中的a的值更改为0x0020之类的值,该值满足第2行的条件。因为这样的输入会导致执行新的代码,所以它被保存到S。在图1b中,这是输入i2。小的字节级突变使CGF能够随后生成满足图1a第3行和第4行分支条件的输入。这是因为有许多可能的解决方案分别满足a>0x1000和b>=0x0123的比较;我们称这些软比较。图1b示出了我们示例中的相应输入:i3和i4。然而,对于CGF来说,生成满足第5行a= =b之类的比较的输入要困难得多;我们称这些为硬比较。输入i1–i4上的随机字节级突变不太可能产生a==b的输入。因此,第6行的代码可能无法在使用常规CGF的合理时间内执行。
现在,考虑另一个测试目标,在这个目标中,我们希望生成最大化通过malloc动态分配的内存量的输入。此目标对于生成压力测试或发现内存不足相关的潜在错误非常有用。CGF算法使我们能够生成在第8行调用malloc语句的输入,例如i4。但是,这个输入只分配0x1220字节(即,刚刚超过4KB)的内存。虽然这个输入上的随机突变可能会产生分配更大内存量的输入,但CGF永远不会保存这些,因为它们的覆盖范围与i4相同。因此,CGF不太可能在合理的时间内发现分配输入的最大内存。

航路点
如果我们将一些有用的中间输入保存到S,而不管它们是否增加了代码覆盖率,上面列出的两个挑战都可以解决。然后,这些中间输入的随机突变会产生输入突变我们称这些中间输入点. 例如,为了克服像a= =b这样的困难比较,我们希望如果中间输入最大化a和b之间的公共位的数量,就可以节省中间输入。我们把这个策略称为cmp。图1b中的蓝框显示了当对航路点使用cmp策略时可以保存到S的输入。在这种策略中,输入i5和i6被保存到S,即使它们没有实现新的代码覆盖率。现在,输入i6可以很容易地变为输入i7,它满足条件a==b。因此,我们很容易在图1a的第6行发现触发中止的输入。类似地,为了实现最大化内存分配的目标,我们保存在给定的malloc调用中分配比S中任何其他输入更多内存的航路点。图1b显示了可以用这种策略(称为mem)保存的示例航路点i8和i9。从i9到i10的虚线箭头表示,经过几个这样的路径点,随机突变最终将导致我们生成输入i10。这个输入导致测试程序在第8行分配最大可能的内存,这几乎是64KB。
现在考虑的条件是,在图1a中,条件是改变,而不是图1a的条件。我们需要在第一个0x8行调用malloc来克服这个问题。我们可以将两种保存路径点的策略组合如下:如果一个新的输入i增加了硬比较操作数之间的公共位数量,或者增加了在调用malloc时分配的内存量,那么就保存一个新的输入i。在第4.7节中,我们将演示这些策略的组合如何使我们能够自动生成PNG炸弹和LZ4炸弹,即在分别模糊libpng和libarchive时分配2-4GB内存的微小输入。
我们提出了一个叫做FuzzFactory的框架,它允许用户实现选择路径点的策略。为此,用户指定除了覆盖率信息外,还需要从被测程序的执行中收集哪些自定义反馈。用户还指定了一个函数,用于在输入集合中聚合此类反馈;聚合的反馈用于决定是否应将某个输入视为一个航路点。
接下来我们将描述该框架及其底层算法。该框架使我们能够迅速实施文献中的三个现有战略和四个新的战略,包括一个综合战略。

fuzzfactory:一个特定于领域的FUZZING框架

我们的目标是构建一个框架,允许用户通过简单地定义一个自定义谓词:is_waypoint(i,S,d) 来构建一个特定于域的模糊应用程序d。谓词告诉模糊器一个新的输入i是否是一个航路点;也就是说,我是否应该被保存到一组已保存的输入S中,以便以后可以对其进行变异以生成新的输入。
在传统的CGF算法中,是否保存输入的决定是根据程序对输入i的动态行为来定义的。具体地说,如果程序对输入i的覆盖包括一个覆盖点,该覆盖点不存在于程序对S中的输入累积获得的覆盖中,则CGF认为i是有趣的并将其保存到S。这个决定是基于一种特定的反馈(即覆盖率)来决定的。反馈直接关系到CGF的目标,即增加程序的覆盖率。
虽然提高代码覆盖率对于发现新的程序行为很重要,但我们相信,如果以其他测试目标为指导,模糊器可以变得更加有效和多样化,例如:发现性能瓶颈或内存使用问题,覆盖最近修改的代码,执行有效的输入行为,等等。
fuzzfactory允许用户原型化以用户定义的自定义目标为目标的fuzzer。支持自定义或特定于域的目标,用户需要指定:(1)从程序执行过程中收集的对任何输入的反馈的具体类型,以及(2)如何使用此反馈来确定输入是否应该被认为是有趣的和保存的。
接下来,我们将描述FuzzFactory用户指定他们希望从执行中获得的特定于域的反馈的机制。然后我们解释is_航路点谓词如何使用这种自定义反馈来确定是否需要保存输入。我们还描述了如何撰写此类领域特定的反馈。最后,我们展示了如何扩展算法1中的CGF算法来考虑特定领域的反馈。
在这里插入图片描述
算法2描述了在FuzzFactory中实现的领域特定的模糊化算法。该算法对算法1中描述的传统覆盖引导模糊算法进行了扩展,并用灰色背景进行了标记。扩展非常简单:在对输入i′执行程序p的过程中,该算法不仅收集覆盖率,还收集域特定的反馈映射dsf1i’,…,dsf|D|i’,用于D中的每个域。然后它在调用is_waypoint(i’,S,D)中使用这些映射来确定是否应该将新的输入i’添加到保存的输入S集合中。

特定领域的模糊应用

我们通过实例化六个独立的特定于领域的模糊化应用程序来演示FuzzFactory的适用性。其中一些模糊算法已经在以前的工作中提出并实现。我们实现这些算法的动机是评估我们是否可以在我们的框架中原型化这些算法,而不改变底层的模糊算法或搜索启发式。第4.1节通过4.6按照复杂性的增加顺序描述六个领域:
(1)slow:基于SlowFuzz[Petsioset al。2017b年]。这是FuzzFactory中实现的最简单的领域。
(2)perf:基于PerfFuzz,通过最大化基本块执行计数来发现热点的应用程序[Lemieux et al.2018]。在Fuzzfactory,这自然概括了slow。
(3)mem:一种新的应用程序,用于生成最大化动态内存分配的输入。
(4)valid:有效性模糊算法的一个应用[Padhyeetal et al.2019b,c],这意味着将输入生成偏向于满足特定程序有效性检查的输入。
(5)cmp:一个平滑硬比较的领域。虽然之前的很多工作都是针对这个应用,但是我们的特定解决方案策略是新颖的。
(6)diff:测试中代码更改后增量模糊化的一个新应用程序。
对于每个应用程序,(1)我们定义了元组(K,V,A,a0,?)的领域术语,(2)我们在表2定义的一些实用程序的帮助下,描述了我们如何在输入i1上的测试执行期间插入测试程序以填充映射dsfi,以及(3)我们报告了将特定于领域的模糊实现应用于一组实际程序的结果。
在这里插入图片描述
表1。实现每个特定于域的模糊化应用程序所需的代码行(LoC)。对于实现inFuzzFactory,该表统计实现编译时指令插入(LoCinst)、运行时支持代码(LoCrt)和reducer函数(LoC?)的C代码,以及这三个数字的总和(LoCtotal)。对于重新实现或先前工作的域,我们通过与扩展的底层覆盖率引导模糊器进行比较,列出了相应独立实现添加或修改的代码行(LOCext)。使用CLOC1进行的所有测量。74; 忽略空白行和仅注释行。

作品。FuzzFactory的一个关键优势是,它使我们能够自然地组合多个特定于领域的模糊应用程序,而无需额外的努力。在第4.7节,我们描述了CMP和MEM的组成,它平滑了硬比较,以加剧内存分配。值得注意的是,我们发现这样一个组合比它的各个部分的总和表现得更好。
在这里插入图片描述
表2。用于注入更新特定于域的反馈映射的代码的插装函数的定义。它们在表3至表8中使用。当在编译时通过被测程序时遇到相应的语法对象时,钩子被激活。这些钩子的处理程序逻辑可以在被测试的程序中注入代码。操作是用于在检测期间实际注入代码的函数。实用程序函数在编译时可用于处理程序逻辑。

实施传统上,实现每一个这样的领域都需要修改模糊工具(如AFL)以实现特定领域的目标。使用FuzzFactory,上述六个域中的四个域可以用不到30行C代码实现。表1列出了我们在本文中使用FuzzFactory实现六个域中的每一个域所需的代码行。第6节提供了有关我们实现的更多细节。对于重新实现先前工作的域,该表还列出了实现相应的专用独立模糊工具所需的代码行。程序检测。第4。1节通过4。6描述如何使用测试程序来实现本文介绍的六个领域中的每一个。当在输入i上执行测试程序时,仪器能够在map dsfi中收集特定于域的反馈。这种插装是在编译时执行的。尽管我们的实现在LLVM IR级别执行插装,但为了便于表示,我们在更高的抽象级别上描述了六个域中每个域的插装逻辑。表2列出了一些钩子、动作和实用程序函数,这些函数是我们在特定于领域的工具的抽象描述中使用的。接下来,我们将描述如何解释表2中的信息。

每当在通过测试程序时遇到程序中的相应元素时,检测框架(如LLVM)就会在编译时激活钩子。例如,在编译时为程序中的每个函数调用表达式调用Func_call(name,args)钩子。这里,name是一个字符串,是对构成函数调用参数的语法表达式的引用列表。检测过程(比如我们为每个模糊域编写的过程)指定了一些处理此类钩子的逻辑。处理程序逻辑可以选择在钩子当前激活的程序元素之前或之后插入新代码。例如,处理程序func_call可以静态地查看名称(sayf),以决定是否在调用tof周围插入代码。代码是通过调用insert_after和INSERT_before插入的。插入的代码可以使用编译时常量,也可以引用静态程序元素,例如:一个或多个参数tof、全局变量或用户定义的函数。为了便于演示,我们将把插入的代码显示为源代码级伪代码,而不是某些IR中的指令。通常,我们会插入更新SFIMAP的代码。实际上,我们会插入一条指令来调用第6节中列出的API之一。1.处理器逻辑是不受限制的;在我们的实现中,使用LLVMAPI的是任意C代码。处理程序逻辑可以利用FuzzFactory在编译时提供的有用函数。表2仅列出了描述本文所述六个领域所需的挂钩和实用功能(表3-8)。为了实现新的域,还可以检测其他语言构造,如分支、加载、存储等。

slow:最大化执行路径长度

模糊测试可用于生成加剧被测程序算法复杂性的输入。SlowFuzz[Petsios et al.2017b]使用资源引导的进化搜索算法引入了这一想法。搜索使用一个适应度函数,该函数统计在执行单个测试输入期间执行的基本块的数量。我们把这个度量称为执行路径长度。
我们的第一个特定于领域的模糊应用程序是SlowFuzz到我们框架的一个端口。该应用程序的目标是生成最大化被测程序执行路径长度的输入。我们希望将其is_waypoint(i,S,d)谓词定义如下:如果输入的执行导致比任何其他输入的路径长度更高,则应保存该输入。

perf:发现热点

PerfFuzz[Lemieux et al.2018]是另一种使用模糊测试生成具有病理性能的输入的工具。与SlowFuzz不同,它最大化了单个条件–执行路径长度PerfFuzz独立地最大化了被测程序中每个基本块的执行计数。为此,PerfFuzz扩展了覆盖率引导的模糊算法,以在新生成的输入增加任何基本块的最大观察执行计数时保存这些输入。在这个域中,目标是找到多次执行相同基本块的输入

mem:加剧内存分配

现在,我们描述了FuzzFactory的一个新应用:生成加剧内存分配的输入。对于这样一个域,有几个用例,比如发现被测程序可以为给定大小的输入动态分配内存的最大数量,发现可能导致与内存不足条件相关的错误的输入,或者为基准测试目的生成内存压力测试语料库

valid:有效性模糊

与CGF相关的一个主要问题是,大多数随机生成的输入无效;也就是说,它们导致测试程序以错误状态提前退出。例如,libpng上的传统CGF不太可能生成许多有效的PNG图像,即使在开始时使用有效输入进行模糊处理。新生成的输入实现的大部分代码覆盖率位于处理输入验证和错误报告的代码路径中。因此,CGF算法很难有效地测试和发现这些程序的主要功能中的错误。在许多情况下,生成最大化代码覆盖率的有效输入是可取的。例如,您可能希望测试一些程序,例如下载和处理在社交媒体网站上上载的文件的图像查看器和媒体播放器。最有可能的是,此类网站不允许用户上传无效文件。图像查看器或媒体播放器中的错误只会在处理有效文件的过程中出现。
有效性模糊[Padhye et al.2019c]最近被提出用于解决生成有效输入的问题。在有效性模糊中,测试程序被扩充以返回关于根据某些特定于程序的有效性概念,输入是否有效,输入文件是否为有效的PNG文件。在模糊循环期间,新生成的输入被保存(1)如果它们增加了总体代码覆盖率,或者(2)如果新生成的输入是有效的,并且它覆盖了任何先前生成的有效输入未覆盖的代码。第一个标准允许保存中间输入,而不管其有效性如何,只要它们产生新的累积代码覆盖率。我们希望通过对这些输入进行变异,可以在以后生成更有趣的有效输入。第二个标准试图最大化有效输入中的代码覆盖率。其他研究人员也使用项目特定有效性的概念指导模糊搜索生成更有效的输入[Laeufer et al.2018;Pham et al.2018]。
在我们现在如何实现模糊算法的有效性框架。第一,我们修改了基准测试套件附带的测试驱动程序,以添加程序特定假设(expr)语句。assume的语义类似于更熟悉的assert的语义:如果参数expr在运行时求值为true,则该语句为no-op;否则,将停止测试执行。图5展示了我们对libpng测试驱动程序所做的三个单行更改之一。我们不必因为无效的PNG头而提前退出,而是简单地用一个假设语句包装有效性检查。除了boringssl之外,我们能够对所有基准测试驱动程序进行如此微小的更改。在我们修改了驱动程序的五个基准测试中,我们添加了1-3个假设语句,将现有的有效性检查包装在测试驱动程序中,更改了1-11行代码。第二,我们插入测试程序,用测试执行期间的代码覆盖率信息填充DSF映射,类似于传统的覆盖率指南模糊化。在运行时,如果要假定的任何参数的计算结果为false,则整个DSF映射将在退出之前重置为初始状态。因此,当且仅当测试输入有效时,DSF映射才会镜像传统的代码覆盖信息。无效输入不产生特定于域的内容反馈。这个scheme会导致算法2的以下行为:如果新生成的输入导致新的累积代码覆盖率,或者输入有效并实现更多的代码覆盖率(即。,更改聚合域特定反馈)而不是任何其他有效输入(即,在产生特定域反馈的输入之间)。

cmp:平滑硬比较

接下来,我们将描述一个著名问题的新解决方案,即硬比较。回想一下图1中的激励示例,它要求生成彼此相等的输入A和B。对于CGF来说,遇到TRNCMP、memcmp和Switch Case语句等操作时也会遇到类似的障碍。过去,一些研究人员已经解决了难以比较的问题[LafIntel2016;Li等人2017;Peng等人2018;Rawat等人2017;Stephens等人2016;Yun等人2018]。该问题的常见解决方案包括但不限于:(1)从已经满足大多数复杂不变量的种子输入开始,(2)从测试程序中挖掘神奇常数,如0x0123,然后作为变异过程的一部分随机插入这些值,(3)将测试程序转换为“展开”将ann字节比较转换为执行1字节比较的一系列分支,(4)执行复杂的静态分析、动态污染分析或符号执行,以识别和克服困难的比较。有些解决方案,如静态挖掘魔法常数或展开多字节比较,确实不适用于可变长度参数的硬比较,例如memcmp(a、b、n),其中所有操作数均从程序输入派生。

diff:增量模糊化

现在我们描述fuzzfactory的另一个新应用:代码更改后的增量模糊化。
通常的做法是让模糊工具运行数小时或数天,以便发现复杂软件不稳定版本的错误。但是,如果开发人员对这样的软件进行了更改,那么目前还没有一种简单的方法可以快速地对更改进行模糊测试。他们可以使用软件上一个版本上长时间运行的模糊会话生成的测试语料库作为回归测试套件,但是这些输入可能不会执行受软件更改影响的代码路径。他们还可以用先前生成的输入语料库作为初始种子启动一个新的模糊会话。然而,他们无法与模糊引擎沟通,模糊引擎应该关注影响软件变更的代码路径。定向模糊工具,如AFLGo[Böhme et al。2017年]解决这个应用程序,但可能需要几个小时的静态分析来预先计算到目标程序位置3的距离。perfuick在这样的连续集成环境中可能不希望每次都使用这种连续的回归方法。
为此,我们提出并实现了一个特定于领域的模糊应用程序来进行增量模糊化。这个应用程序的目标是引导模糊化快速发现访问刚刚修改过的代码行的有趣的代码路径.我们将修改后的代码行集称为diff。为了测量输入执行的路径的多样性,我们将重点关注基本块转换(bbt),而不是仅关注基本块。

组成多个域

由于特定领域的反馈映射和底层模糊算法之间的明确分离,我们可以很容易地在同一测试程序二进制文件中组合多个特定领域的模糊应用程序。组合两个特定于域的模糊应用程序只需要合并与每个域相关联的工具。我们的每个域的编译标志都是简单的。每个域的关联检测只更新自己的DSF映射。类似,我们的领域特定的模糊算法聚合从每个注册的域独立反馈(参考算法2)。

讨论

我们的框架允许开发人员和研究人员通过定义一种策略来有选择地保存中间输入来控制模糊测试的过程。我们的框架目前没有提供任何显式的钩子到CGF算法中使用的各种其他搜索启发式方法,例如变异运算符或种子选择策略。原则上,应该可以移植通用的启发式方法,例如AFLFast中使用的启发式方法[Böhme et al。或FairFuzz[Lemieux and Sen 2018]或FairFuzz[Lemieux and Sen 2018]来处理本文中描述的各种特定领域的模糊应用程序。改进通用模糊启发式算法的工作与本文的工作是正交的我们的贡献主要贡献是提出了模糊算法和被测程序反馈选择之间的关注点分离。
理论上,代码覆盖率的基本增加本身可以被认为是特定于领域的反馈。那我们可以定义一个域d,当输入i导致执行S中没有任何输入覆盖的代码时,is_waypoint(i,S,d)满足。然而,在算法2中,我们总是在增加代码覆盖率的情况下保存一个输入,而不是通过另一个域来建模这个标准。在实践中,我们发现增加代码覆盖率对所有领域都是有用的,因为这会导致发现新的程序行为。换句话说,我们总是用一个试图最大化代码覆盖率的默认域组合每个自定义域。如果需要,我们的实现允许通过环境变量禁用默认域。
自从我们完成本文的实验以来,甚至出现了更专业的适合我们抽象的路径点的模糊器:例如(1)Coppik等人。[2019]将读/写新值的输入保存到输入相关内存地址,以及(2)Nilizadeh等人。[2019]通过保存执行路径与参考路径最大不同的输入,发现侧信道漏洞。我们对这样的工作感到鼓舞,因为它加强了模糊工厂的理由。

实施

我们实现了FuzzFactory作为AFL的扩展。在FuzzFactory中,特定领域的模糊应用程序是通过插入测试程序来实现的。表1描述了实现本文描述的六个域中的每一个所需的代码行。在我们的应用程序中,我们使用LLVM执行检测。然而,测试程序也可以使用任何其他工具来检测,比如英特尔的Pin[Luk等人。2005年]。事实上,特定领域的模糊应用程序可以通过手动编辑测试程序来添加调用FuzzFactoryAPI的代码来实现。接下来我们将描述这个API。

特定领域模糊化API

在这里插入图片描述

图11概述了FuzzFactory提供的API。类型dsf\t定义域特定映射的类型。在我们的实现中,键和值总是32位无符号的整数。但是,用户可以指定DSF映射的大小;也就是说,它将指定的键数包含。TheAPI函数new_domain注册一个新域,其密钥集K包含key_size密钥。
参数reduce和a_0分别提供reducer函数(intxint->int类型)和初始聚合值。对于慢速域,key_size为1。对于K是一组程序位置L的应用程序,我们使用216的密钥大小,并将16位伪随机数分配给基本块位置,类似于AFL。对于增量模糊应用,其中K=L×L,我们使用哈希函数将两个基本块位置组合成一个整数值密钥。集合V和A是通过使用DSF映射和reduce函数的实现来隐式定义的。对于有效性模糊等应用,其中A是一个数量级的集合,我们使用位向量来表示集合。
函数new_domain返回DSF映射的句柄,然后在图11中列出的后续tap中使用该句柄,例如DSF_increment。在执行任何测试之前,在测试程序启动时插入对新_域的调用。用户需要确保提供的reducerfunction满足属性1和2,这反过来又保证了单调聚合(定理1)。以“dsf”开头的API函数操作dsf映射。参数键必须在范围[0,key_size)中。

相关工作

据我们所知,Fuzzfactory是第一个实现特定领域模糊应用程序的框架。JQF[Padhye等人,2019a]允许用户为Java实现自定义模糊算法;然而,插装是固定的,而搜索算法可以定制。基于LLVM的Clang编译器[Lattner和Adve2004]为C/C程序提供了一个可定制的跟踪框架。通过使用命令行标志,例如-fsanizize覆盖率,可以要求Clang插入基本块,并通过比较操作调用特殊命名的函数;用户可以链接这些函数的自定义实现以跟踪程序执行。LibFuzzer[LLVM Developer Group2016]使用这些钩子提供来自被测程序的反馈,以便执行覆盖率引导的模糊化。然而,Libfuzzer不提供使用自定义聚合函数提供任意特定于域的反馈的机制。也就是说,尽管LLVM提供了程序执行的挂钩,但目前还无法将信息传递给模糊算法。然而,使用LLVM的跟踪钩子调用FuzzFactory的API进行特定于域的模糊化相对容易。
正如Manès等人[2018]所调查的那样,模糊测试领域的大量研究目标是搜索过程中的通用改进。这些技术通常采用模糊算法中使用的各种启发式方法[Böhme et al.2016;Chen和Chen2018;Lemieux和Sen2018],或寻求将模糊测试与重量级方法相结合,如concolic执行[Ognawala et al.2018;Stephens et al.2016;Yun et al.2018]。我们提出的设计与这些技术中的任何一种都没有冲突。模糊过程的通用调整可应用于算法2,而不影响收集特定领域反馈的机制。
结构化模糊化工具,如protobuf mutators[Serebryany等人2017]、AFLSmart[Pham等人2018]、Nautilus[Aschermann等人2019]和Superion[Junjie Wang和Liu2019]利用了与测试程序预期输入格式相关的领域特定信息。这些方法可以与第4。4节中介绍的有效性模糊域相结合。克服我们观察到的XML等格式的局限性[Padhye等人,2019b]。

结论

我们提出了FuzzFactory,一个实现特定领域模糊化的框架应用程序。我们的框架提供了一种机制,用于在被测程序执行期间与模糊引擎通信、任意特定于域的反馈。我们对六个前端的实验表明,FuzzFactory可以用来原型领域特定的应用程序,而不需要改变底层的搜索算法。特定领域反馈的有效性取决于测试程序的性质、目标和初始种子输入。我们希望,我们提出的框架将使研究人员能够快速开发出高度专业化的领域专用解决方案,并推动最新技术的发展。

使用

文档和例子
本节假定对 AFL 有一定的熟悉程度。
要建立 FuzzFactory 的自定义 afl-fuzz,请在项目根目录下运行 make。
你可以像以前一样使用这个 afl-fuzz 来模糊常规的 AFL 仪表程序。
你也可以使用这个带有 -p 选项的 afl-fuzz 来模糊那些被工具化(选项 1)或被修改(选项 2)的程序,以调用 FuzzFactory 的 API。

选项1:通过基于LLVM的工具进行特定领域的反馈
要构建 FuzzFactory 基于 LLVM 的特定领域仪器,请在项目根目录下运行 make llvm-domains;你需要安装 LLVM/Clang 6+(AFL 需要找到 llvm_config)。这将建立一个特殊版本的 afl-clang-fast,支持特定领域的仪器化传递,作为插件。这条命令还可以构建FuzzFactory中的六个特定领域的仪器传递;它们与论文中列出的六个领域相对应:slow, perf, mem, valid, cmp, diff。
FuzzFactory提供了一种扩展机制,可以快速实现调用FuzzFactory API的LLVM工具传递;参见llvm_mode/fuzzfactory.hpp和llvm_mode中的以下六个领域实现。
域slow(受SlowFuzz启发)。
waypoints-slow-pass.cc:实现了论文中表3所述的慢速域。
waypoints-slow-rt.c: 为慢速分配DSF地图。
域perf(PerfFuzz的移植)。
waypoints-perf-pass.cc:执行本文表4中描述的领域perf。
waypoints-perf-rt.c: 为perf分配DSF地图。
域mem(malloc/calloc fuzzer)。
waypoints-mem-pass.cc:执行论文表5中描述的域内存。
waypoints-mem-rt.c: 为mem分配DSF地图。
域valid(Zest-v的端口)。
waypoints-valid-pass.cc:执行本文表6中描述的域名有效性。
waypoints-valid-rt.c: 为慢速分配DSF地图,并定义了ASSUME()的参数为假时的逻辑。
域cmp(魔术字节和校验码模糊器)。
waypoints-cmp-pass.cc:实现了论文中表7所述的域cmp。
waypoints-cmp-rt.c: 为cmp分配DSF图,同时定义了所有的wrapcmp函数,这些函数执行共位计算并相应地更新DSF图。
域diff(增量模糊器)。
waypoints-diff-pass.cc:实现了论文中表8中描述的领域差异。
waypoints-diff-rt.c: 分配域差分使用的globals。

使用FuzzFactory的基于LLVM的域进行模糊处理
本节假设你已经安装了LLVM/Clang,并在根目录下运行了make llvm-domains。
目录中的demo包含一个单文件的测试程序(demo.c),以说明FuzzFactory的使用。在本节的剩余部分,请切换到该目录。

cd demo

背景。这就是你用常规AFL编译demo.c的方法。

.../afl-clang-fast demo.c -o demo

这就是你如何用FuzzFactory编译demo.c,使用mem域。

WAYPOINTS=mem .../afl-clang-fast demo.c -o demo

这是使用FuzzFactory编译Demo.c时,使用cmp域的方法。

WAYPOINTS=cmp .../afl-clang-fast demo.c -o demo

这是你如何用FuzzFactory编译demo.c,使用cmp和mem域的组合。

WAYPOINTS=cmp,mem .../afl-clang-fast demo.c -o demo

现在,让我们使用种子子目录中的种子文件对演示程序进行模糊处理。不管用什么域来检测测试程序,同样的命令都适用。

./afl-fuzz -p -i seeds -o results ./demo

如果你对一个已经用cmp+mem域检测的程序进行了模糊处理,在模糊处理开始之前,你会在AFL输出中看到以下内容。

[+] 2 domain-specific front-end configs received
DSF 0: Start=0x000000, End=0x010000, Size=65536, Reducer[0]=MAX, Initial=0
DSF 1: Start=0x010000, End=0x010400, Size=1024, Reducer[0]=MAX, Initial=0

这表明测试程序已经向FuzzFactory注册了两个特定领域的反馈图。
其余的模糊测试环节与平时运行AFL类似。按CTRL+C来停止模糊测试。在摸索过程中,会创建以下日志文件,其中有关于特定领域反馈的粗略输出:results/fuzzfactory.log。

通过LLVM仪器化的新领域
为了实现你自己的特定域的仪器化传递,让我们称它为域foo。(1) 在llvm_mode目录下创建文件waypoints-foo-pass.cc和waypoints-foo-rt.c,(2) 在根目录下运行make llvm-domains DOMAINS+=foo,(3) 在设置环境var WAYPOINTS=foo后用afl-clang-fast编译测试程序。为了帮助创建通行证和运行时文件,可以使用上一节列出的实现作为模板。

选项2:通过手动调用API进行特定领域的反馈
FuzzFactory也可用于手动增强测试程序并指定特定领域的测试目标。只需包括waypoints.h并在你的测试程序中使用以下宏。

/** 
 * Creates a new DSF map `name` with `size` keys, `reducer` function, and `initial` aggregate value.
 *
 * To be called at the top-level global scope.
 */
FUZZFACTORY_DSF_NEW(name, size, reducer, initial)

/** Set dsf[k] = max(dsf[k], v); */
FUZZFACTORY_DSF_MAX(dsf, k, v)

/** Set dsf[k] = dsf[k] | v; */
FUZZFACTORY_DSF_BIT(dsf, k, v)

/** Set dsf[k] = v; */
FUZZFACTORY_DSF_SET(dsf, k, v)

/** Set dsf[k] = dsf[k] + v; */
FUZZFACTORY_DSF_INC(dsf, k, v)

使用Clang的演示
本节假设你已经安装了LLVM/Clang,并且在根目录下运行了make llvm-domains。

要从一个测试程序中看到这些宏的使用示例,请cd到demo目录并运行以下程序。

diff demo.c demo-manual.c # 比较演示程序和fuzzfactory-macro增强的测试程序

编译增强的测试程序,如下所示。

../afl-clang-fast demo-manual.c -o demo-manual

使用-p选项对增强后的测试程序进行模糊处理,以启用特定领域的模糊处理。

../afl-fuzz -p -i seeds/ -o results ./demo-manual

在启用cmp和mem两个航点的情况下,对增强的程序进行模糊处理与对原始演示程序的模糊处理类似。同样,你会看到这样的输出。

[+] 1 domain-specific front-end configs received
DSF 0: Start=0x000000, End=0x000004, Size=4, Reducer[0]=0x55a947da28f0, Initial=0

以及AFL状态屏幕的开始。

从保存的输入中分析特定领域的信息
除了寻找崩溃(bug)之外,我们还经常对生成优化某些特定领域指标的输入感兴趣。例如,在用FuzzFactory中的PerfFuzz实例化的perf域进行模糊处理后,我们想找到所有保存的输入中的最大循环数。无论是为了这个目的,还是仅仅为了调试你的特定领域仪器,FuzzFactory都提供了一个名为afl-showdsf的实用工具,可以分析来自一个或多个保存的输入的特定领域反馈。
运行 afl-showdsf,不需要任何参数就可以看到它的用法。
**重放单一输入。**运行 afl-showdsf,然后再运行测试程序及其输入的命令,以查看单个执行的 DSF。从前面几节提到的演示目录中,运行。

./afl-showdsf ./demo-manual < seeds/zerozero.txt 

这将输出一个与 FuzzFactory 的 afl-fuzz 类似的标题,然后是在输入上运行测试程序本身的输出。

[+] 1 domain-specific front-end configs received
DSF 0: Start=0x000000, End=0x000004, Size=4, Reducer[0]=MAX, Initial=0
Total DSF map length = 4
170920496, 0
Demo: Reached point A
Demo: Reached point B

这个输出后面会有一串形式的行。
dsf[k] = v
表示在执行过程中,输入触发了键k的特定领域反馈值v。只有那些v不是初始聚合值的键k才会被显示。
在上面的例子中,我们将看到一个单行。
dsf[0] = 19
这告诉我们,在运行种子输入时,键0的比较在操作数之间有19位的共同点(见demo-manual.c)。

**跨输入的聚合。**运行 afl-showdsf 与 -i

来执行一个目录中的所有输入,并使用在每个领域注册的还原函数来聚合它们的特定领域反馈(例如,MAX用于领域perf)。在demo目录中,用-p对demo-manual进行模糊处理一段时间后,运行。

./afl-showdsf -i results/queue/ -- ./demo-manual

同样,这将输出一个与常规 afl-fuzz 类似的标题,以及一个形式的行列表。
dsf[k] = v
这一次,每个条目代表了目录中所有输入的集合。对于特定领域反馈图中的每个键k,看到的相应的聚合反馈值是v。

Receiving 1 domain-specific front-ends..
[+] 1 domain-specific front-end configs received
DSF 0: Start=0x000000, End=0x000004, Size=4, Reducer[0]=MAX, Initial=0
Total DSF map length = 4
dsf[0] = 32
dsf[1] = 27
dsf[2] = 2147483645

键0的条目告诉我们,至少有一个输入与demo-manual.c中第一次比较的所有32位相匹配,而键2的条目告诉我们,在demo-manual.c分配的最多的内存是2147483645字节。

解释组合式DSF的结果:当用组合式DSF的程序运行afl-showdsf时,例如上面的WAYPOINTS=cmp,mem例子和程序demo,我们可以根据头的输出来区分不同领域的DSF值。例如,在运行:

./afl-showdsf -i results/queue/ -- ./demo

我们会看到这样的输出。

DSF 0: Start=0x000000, End=0x010000, Size=65536, Reducer[0]=MAX, Initial=0
DSF 1: Start=0x010000, End=0x010400, Size=1024, Reducer[0]=MAX, Initial=0
Total DSF map length = 66560
dsf[7109] = 32
dsf[21658] = 32
dsf[33477] = 27
dsf[65690] = 2147483645

我们看到两个dsf值:dsf[7109] = 30和dsf[21658] = 32。我们可能想问的一个问题是,这些值是来自cmp还是mem域。

头部的信息有助于我们区分这些域。

DSF 0: Start=0x000000, End=0x010000, Size=65536, Reducer[0]=MAX, Initial=0
表示从0x000000到0x010000的所有键,不包括最后一个键(即[0,65536]),属于第一个域(cmp)。第二行。

DSF 1: Start=0x010000, End=0x010400, Size=1024, Reducer[0] =MAX, Initial=0
表示从0x010000到0x010400的所有键,不包括最后一个键(即[65536,66560]),都属于第二个DSF(mem)。

因此,两个打印出来的值dsf[7109]=32和dsf[21658]=32都属于cmp域,并表明这些比较被超越了。dsf[65690] = 2147483645这个值显示了一个来自mem域的条目(因为键在[65536, 66560]范围内),并表明在某个malloc()的最大内存分配是2147483645字节。

实现https://www.c0d3xpl0it.com/2019/12/domain-specific-fuzzing-with-waypoints.html有好心人截图发我看看吗

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值