Group of Software Security In Progress

GoSSIP @ LoCCS.Shanghai Jiao Tong University

IJON: Exploring Deep State Spaces via Fuzzing

作者:Cornelius Aschermann, Sergej Schumilo, Ali Abbasi, and Thorsten Holz

单位:Ruhr University Bochum

出处:2020 IEEE Symposium on Security and Privacy (S&P 20)

原文:https://www.syssec.ruhr-uni-bochum.de/media/emma/veroeffentlichungen/2020/02/27/IJON-Oakland20.pdf

1 Abstract

目前fuzzing的技术仍无法全自动地、高效地解决复杂状态机的问题。当前大多数方法仅限于添加字典或新的种子作为输入来引导fuzzer。在处理复杂程序时,这些“人机交互”机制无法发现代码库的新部分。

在本文中,作者提出了IJON,这是一种注释机制(annotation mechanism),分析人员可以使用它来指导fuzzer。与上述两种技术相比,此方法可以根据代表程序内部状态的数据对程序的行为进行更系统的探索,使得用户可以使用很少的注释(通常是一行)来帮助fuzzer解决以前无法解决的挑战。作者扩展了各种基于AFL的fuzzer,使其能够用指导提示来注释目标应用程序的源代码。

评估表明,这种简单的注释能够解决当前基于fuzzer或符号执行的工具无法克服的问题。例如,通过作者的扩展,fuzzer能够玩并解决诸如Super Mario Bros.之类的游戏,或者解决诸如哈希映射查找之类的更复杂模式。为了进一步展示注释的功能,作者结合使用AFL和IJON来发现新颖的安全性问题以及以前需要自定义和全面语法的问题。最后,证明使用IJON和AFL可以解决CGC数据集中的许多挑战,这些数据集可以抵抗目前为止的所有全自动和人工指导的尝试。

1 Introduction

近年来,fuzzing的研究重心放在了进一步改进fuzzing方法上,通常是为了获得更大的代码覆盖率并更深入地研究给定的软件应用程序。但是,仍然存在大量的open challenge,无论是symbolic/concolic execution还是fuzzing都难以解决。很大程度上是因为在一般情况下根本无法确定潜在的问题,即fuzzing的方向性。

本文针对的问题:探索复杂的状态机

当前fuzzing的方法难以探索复杂的状态机。这些状态机的状态变化主要反映在程序状态数据的更改中。由于状态数据的更新都是由某些代码触发的,因此coverage-based fuzzer能够地探索每个单独的更新。但是如果先前已观察到所有单独的更新,则没有反馈可奖励探索导致新状态的不同更新的组合,使得fuzzer很难产生特定的更新顺序来发现错误。

类似地,因为确切的更新顺序(以及因此选择的精确代码路径)对于发现错误至关重要,所以concolic execution也会失败。由于concolic execution将路径固定为观察到的执行路径,因此求解器无法获得触发目标条件的输入。最后,如果状态空间变得太大,那么即使可以自由探索不同路径的完全符号执行也会失败。

有一种复杂的解决方案仅基于符号执行来支持最少的环境/指令集,以克服fuzzing中的挑战。然而此类方法无法很好地扩展到复杂的应用程序,成本较高,工业界很少使用。

本文中

“Computers are incredibly fast, accurate and stupid; humans are incredibly slow, inaccurate and brilliant; together they are powerful beyond imagination”

作者探索了分析人员如何操纵fuzzer来克服当前的挑战。主要基于两点观察的启发: * 相比计算机,分析人员更擅长制定high level的战略计划,而计算机胜在执行力。所以在很多领域中常常使用 “human-in-the-loop” 这种方法。 * 许多模糊测试从业人员在其fuzzing过程中使用封闭的反馈回路:首先,运行fuzzer一段时间,然后分析代码覆盖率。经过手动分析后,调整fuzzing过程以增加覆盖率。改善fuzzing性能的常见策略包括从目标应用程序中删除具有挑战性的部分(e.g.,校验和)、更改mutation策略或显式添加输入样本以解决fuzzer无法自动生成的某些约束。

作者相信,通过辅助和控制fuzzing过程,与fuzzer进行交互的人员可以大大提高fuzzing分析应用程序的能力,并克服当前的许多障碍。具体来说,作者将重点放在一类特殊的挑战上:当前的fuzzer无法在代码覆盖率之外正确探索程序的状态空间(无法探索导致相同代码覆盖率但状态值不同的程序执行)。作者的方法中,分析人员可以注释那些更应该彻底地探索的状态空间的部分,从而修改fuzzer的反馈功能。

为了证明方法的可行性,作者扩展了各种基于AFL的fuzzer,通过注释目标应用程序以指导fuzzer。作者将其扩展命名为IJON(Stanislaw Lem书中著名的太空探险家Ijon Tichy)。四个案例表明注释可以帮助克服重大障碍并探索更多有趣的行为。例如,使用简单的注释,可以玩Super Mario Bros.,并可以从CGC挑战数据集中解决困难的情况。

作者作出了以下贡献:

  • 系统地分析当前fuzzer实现的反馈方法,研究它们如何表示状态空间,并研究在哪些情况下会失败
  • 为当前feedback fuzzer设计了一组扩展,这些扩展使分析人员可以在应用程序的状态空间中引导fuzzer以解决当前方法无法克服的困难约束
  • 在几个案例研究中演示了如何使用这些注释来探索目标应用程序的更深层行为。作者展示了fuzzer如何有效地探索可信平台模块(TPM)、复杂格式解析器、游戏Super Mario Bros.、迷宫和哈希映射实现的软件仿真器的状态空间。此外,作者证明了其方法能够解决CGC数据集中的一些最困难的挑战,并在实际软件中发现新的漏洞。

IJON的实现和说明、评估结果的完整数据集与案例研究的演示视频:https://github.com/RUB-SysSec/ijon 获得。

2 Technical Background

作者的工作基于AFL家族的fuzzer,它们通常试图从程序的状态空间中找到一个能触发多种不同状态的语料库。这里的状态表示存储器和寄存器的一种配置,以及由OS提供的状态(例如,文件描述符和类似的原语)。状态空间是程序可能处于的所有可能状态的集合。由于状态空间非常庞大,fuzzer必须优化测试test case以达到更多更有趣的状态。此类fuzzer通常使用代码覆盖率来确定输入是否达到与语料库中存在的状态足够不同的状态。

2.1 AFL Coverage Feedback

AFL类型的fuzzer跟踪在每个测试用例输入期间CFG中各个边被执行的频率,插入到目标应用程序中的探针将计算CFG中每条边的频率并将其存储。

  • 局部的共享映射(shared map):该映射会在每次测试运行期间累积所有边缘的计数
  • 全局的位图(bitmap):包含整个fuzzing中遇到的所有边缘覆盖率,用于快速判断测试输入是否触发了新的覆盖率

如果在任何一条边上出现了新的执行次数,AFL就认为这个测试输入是有趣的并将其存储。由于边始终连接两个基本块,因此被编码为包含两个标识符的元组$(id_s,id_t)$。

2.2 Extending Feedback Beyond Coverage

fuzzing有时会卡在搜索空间的一部分中,这是因为没有任何可能的突变会提供新的反馈。先前的一些方法可以将反馈机制扩展到代码覆盖率外,从而避免陷入平稳状态。 * LAF-INTEL 通过将大型比较指令拆分为多个较小的比较指令来解决magic byte类型约束。 * STEELIX 使用动态二进制工具来实现相同的方法,将多字节比较指令拆分为多个单字节指令后,每当操作数的一个字节匹配时,fuzzer便可以查找新的覆盖范围。 * ANGORA 为每个函数分配一个随机标识符。它使用这些标识符将覆盖元组扩展到第三个字段,该字段包含当前执行上下文的哈希值。如果在不同的调用上下文中出现了“相同”的覆盖率,则可以找到它。此方法有助于解决对strcmp函数的多次调用。然而,这种方法的缺点是将所有调用上下文视为唯一的,很可能产生大量实际上并不有趣的输入,也就需要更大的位图。

3 Design

fuzzer会尝试从状态空间的“有趣”区域中尽可能高效地进行采样,但是很难找到一个准确客观的指标来衡量任何给定输入的状态空间有多“有趣”。 AFL家族的整体成功表明遵循边缘覆盖率是识别新的有趣区域的有效指标,因为在大多数程序中,每个新边缘都表示一种特殊情况。但是在某些代码构造中,如果不探索状态空间中的中间点就不可能产生新的覆盖率。而这种中间点需要用户在概念上进行描述来为fuzzer提供附加反馈。 > 实际上是通过用户交互,来提供具有适应性的更有针对性的metric作为feedback

3.1 State Exploration

为了确定使用当前技术难以fuzz的代码结构,作者使用了最新的fuzzer进行实验,并手动检查获得的覆盖率。主要有以下三个问题:

  • 已知的相关状态值: 有时代码覆盖率几乎不产生有关程序状态的信息,因为所有感兴趣的状态都存储在数据中。如果分析人员了解存储有趣状态的变量,则可以将状态直接反馈给fuzzer,然后fuzzer便可以探究导致不同内部状态的输入。

List-1中的代码实现了一个迷宫游戏,玩家在四个方向上移动来探索迷宫,玩家可以退后并留在同一位置。这将在程序中创建大量不同的路径。同时,只有四个分支可以覆盖,因此仅依靠覆盖率并不足以说明一个行为是否有趣。在这个较难的版本中,x和y坐标是需要探索的相关状态,必须考虑x和y的组合才能发现解。由于迷宫很小(最多只能到达几百个不同的x,y对),因此分析人员可以指示fuzzer将任意新对作为新覆盖率。 这类情况在较大的状态空间也是同样的,例如游戏《超级马里奥兄弟》同样主要关心玩家的坐标。但是,玩家坐标的空间比迷宫游戏大得多。因此分析人员需要能够提供一个fuzzer可以实现的目标(如增加x坐标),而不是简单地探索所有不同的状态。这样,fuzzer就可以丢弃次等的中间结果。

  • 已知状态更改: 在某些情况下,用户可能不知道相关状态的哪些部分值得研究或者状态可能散布在整个应用程序中且难以识别。虽然状态可能是已知的,但是不能识别出足够小的子集以用于直接引导fuzzer。 分析人员可以标识预期会更改状态的代码部分。这种情况通常发生在使用高度结构化数据(例如消息序列或文件格式的块列表)的应用程序中。在这种情况下,用户可以创建一个包含消息日志或块类型的变量,而不是直接给出状态本身。此变量可以充当实际状态更改的代理,并且可以暴露给反馈函数(图2.b)。结果,fuzzer现在可以尝试探索这些状态变化的不同组合。在这种情况下,状态更改日志用作真实状态(无法轻易将其暴露给fuzzer)的抽象层。 考虑list-2中所示的调度程序代码,该代码基于许多常见的协议实现。AFL很难生成有趣的消息序列,因为fuzzer无法区分程序状态机中的不同状态。通过使用已成功处理的消息类型的日志,fuzzer能够更有效地探索由消息组合导致的不同状态。

  • 缺少中间状态: 例如magic byte校验,程序的覆盖率和值均未提供相关反馈,因此AFL型fuzzer无法解决它们。尤其在更复杂的情况下,如果输入和最终比较之间的关系变得更加复杂,那么即使concolic execution之类的技术也会失败。但是分析人员通常可以推断程序的行为方式,并且通常可以提供fuzzing进度的指示。通过将该指示符编码为额外的人为中间状态,分析人员可以引导fuzzer。 如list-3,该代码基于objdump二进制文件中的一个困难案例。该程序包含一个执行哈希表查找的功能和一个if条件,其中查找结果用于搜索给定的字符串。更具体地说,首先对密钥进行哈希处理,然后检查相应的存储桶。如果存储桶为空,则不会进行进一步处理。如果存储桶中包含一些值,则将它们进行单独比较。 这对基于concolic execution的工具和fuzzer都构成了重大挑战:解决此约束要求找到路径(即,计算散列所需的循环迭代次数)和所有相同的覆盖范围的hash值。即使找到了确切的路径,仍然依赖于哈希和实际的字符串匹配。fuzzer必须解决比较问题,同时保持输入的哈希值始终等于目标字符串的哈希值。concolic executor很难找到解决此约束的确切路径。 作者发现,在整个binutils代码库中,各种字符串都发生了500次以上的类似情况。在大多数情况下,散列表用输入中的值填充,并查找固定的字符串。分析人员可以认识到这有效地实现了一对多的字符串比较。借助这种观察认识,可以通过将这种复杂的约束转化为一系列简单的字符串比较(可以通过类似LAF-INTEL的反馈来解决)来指导fuzzer找到解决方案。

3.2 Feedback Mechanisms

  • Annotation(patch): 由于当前的fuzzer都无法让分析人员直接向fuzzer提供反馈,因此作者设计了一组注释,以使分析人员能够影响fuzzer的反馈函数,其目标是使分析人员可以通过这些注释为fuzzing过程提供high level的指导。
  • long-running fuzzing session & a second fuzzing session: 分析人员可以确定fuzzer无法取得进展的原因然后开始第二次模糊测试,其重点是使用自定义注释解决该障碍。当fuzzer解决路障时,long-running fuzzing会话会从临时会话中选择会产生新覆盖率的输入,并继续进行模糊测试,从而克服了困难的情况。

为了简化这样的工作流程,作者设计了四个可用于注释源代码的通用原语: 1. 允​​许分析人员选择与解决当前问题相关的代码区域。 2. 允许直接访问AFL位图以存储其他值。bitmap的条目可以直接设置或增加,从而将状态值公开给反馈函数。 3. 分析师能够干预覆盖范围的计算:允许相同的边缘覆盖率导致不同的位图覆盖率,能在不同状态下创建更细粒度的反馈。 4. 引入了一个原语,允许用户添加hill climbing optimization。如果可能状态的空间太大而无法穷举探索,则用户可以为fuzzer提供一个工作目标。

3.3 IJON-Enable

IJON-ENABLEIJON-DISABLE注释可用于启用和禁用覆盖率反馈。这样就可以有效地排除代码库的无用部分,指导fuzzer仅在满足某些条件的情况下探索代码。

3.4 IJON-INC 和 IJON-SET

IJON-INCIJON-SET注释可用于增加或设置位图中的特定条目。这允许将状态中的新值等同于新代码覆盖率。分析人员可以使用此注释将状态的各个方面选择性地暴露给fuzzer使其可以探索该变量的许多不同值。该注释增加了代码覆盖范围之外的反馈机制。使得新数据覆盖率也能使Fuzzer受到奖励。

考虑list-5中所示的注释。每次x更改时,位图中的新条目都会增加(基于当前文件名、当前行号和x值的哈希值计算位图中的索引,然后位图中的相应条目将递增)。

IJON-SET注释不设置条目的增量,而是直接设置位图值的最低有效位。这使得可以控制位图中的特定条目来引导模糊器。先前介绍的所有三种注释方法都使用此原语。

例如在迷宫游戏中添加了单行注释(list-6),它使用x和y坐标的组合作为反馈。于是游戏中任何新访问的位置都被视为新的覆盖范围。

如果无法轻易观察到状态,则可以使用状态更改日志来记录,在其中注释已知会影响我们关心的状态的操作,并将状态更改日志用作反馈的索引。例如list-7中,在成功解析和处理了每条消息之后,将代表消息类型的命令索引附加到状态更改日志中。然后,设置一个由状态更改日志的哈希值寻址的位。每当看到多达四个成功处理的消息的新组合时,模糊器都将输入视为有趣的,从而在应用程序的状态空间中提供了更好的覆盖范围。

3.5 IJON-STATE

如果消息无法轻松联系起来,状态更改日志可能不足以探索不同的状态。为了产生更细粒度的反馈,可以探究状态和代码覆盖率的笛卡尔积。 为了实现这一点,作者提供了IJON-STATE,该原语能够更改边缘覆盖范围本身的计算。作者使用名为“虚拟状态”的第三个组件扩展了边缘元组。 在计算任何边缘的位图索引时,也会考虑此虚拟状态分量。只要虚拟状态发生变化,任何边缘都会触发新的覆盖范围。但是如果虚拟状态的数量增加太多,则fuzzer会被大量输入淹没,这实际上会减慢模的进度。

如list-8,在没有注释的情况下,fuzzer可能难以探究各种消息的组合。通过将协议状态显式添加到fuzzer的虚拟状态,可以根据协议状态创建代码的多个虚拟副本。

因此,模糊器能够全面浏览协议状态机各种状态下的所有可能消息。 实际上,相同的边缘覆盖率现在可以导致不同的位图覆盖率,因此模糊器可以有效地探索被测程序的状态空间。 请注意,为防止状态爆炸,状态只有三个可能的值。 结果,一旦成功通过身份验证,模糊器就可以完全重新开发整个代码库。

3.6 IJON-MAX

前面的主要提供可用于增加所存储输入的多样性的反馈。但是在某些情况下,我们想针对特定目标进行优化或者状态空间太大而无法完全覆盖。在这种情况下,可能不关心一组不同的值,或者想要丢弃所有中间值。为了在这种情况下实现有效的fuzzing,作者提供了一个称为IJON-MAX的最大化原语。它有效地将fuzzing变成了基于爬坡的通用黑盒优化器。为了使多个值最大化,提供了多个(默认为512个)插槽来存储这些值。像coverage位图一样,每个值都独立最大化。使用此原语,也可以通过最大化-x来轻松构建x的最小化原语。

例如游戏《超级马里奥兄弟》,其中玩家在侧向滚动游戏中控制角色。在每个关卡中,目标是到达关卡的末端,同时避免诸如敌人,陷阱和陷阱之类的危险。如果角色被敌人碰触或掉进坑中,游戏结束。为了正确地探索游戏的状态空间,达到每个关卡的末尾很重要。如list-9所示,可以通过要求fuzzer尝试最大化玩家的x坐标来完成关卡。鉴于这是一款横向滚动游戏,可以有效地指导fuzzer找到通过关卡成功完成游戏的方法。

4 Evaluation

作者在五个最先进的fuzzer之上实现了IJON,展示了相同的注释如何根据各种不同的指标来超越作为baseline的fuzzer。作者针对多个不同的目标进行实验,以展示如何仅使用少量注释就能克服各种难题。

5.1 Setup

实验机器配置:Debian 4.9.65-3,Intel Xeon E5-2667(12核),2.9GHz,94GB RAM

作者将未修改的fuzzer与带有注释的fuzzer进行比较。IJON的用例是使任何fuzzer都可以运行,直到(基本)饱和,检查覆盖率,并使用手动注释对其进行改进。在一些实验中,作者选择了一些小例子,并假设只关心解决这个困难的方面。这模拟了使用IJON_ENABLE将fuzzing限制在感兴趣的区域。baseline fuzzer通常无法专注于单个区域,于是在该特定任务上的表现更差,因为它们还会探索目标应用程序的其他部分。

5.2 The Maze – Small, Known State Values

迷宫通常用于演示基于符号执行的测试用例生成的功能。公开发行的版本很简单,这是因为迷宫没有死角且只有正确的举动才能立即终止游戏。Out-of-the-box AFL和类似的fuzzer无法在合理的时间内解决这种迷宫,而KLEE可以在几秒钟内找到解决方案。该游戏的有效状态空间在采取的步骤数上是线性的,并且不会发生状态爆炸。

作者提升了游戏复杂度:玩家可以退后并停留在同一位置(通过走进墙壁)而不会死。结果,状态空间的步数呈指数增长,KLEE也无法解决迷宫问题。此外,作者创建了一个更大的迷宫。

  • 初始运行:作者对大型和小型迷宫都进行了实验。每个实验分别使用两种配置执行3次。在第一种配置中,使用原始的简单规则,任何错误的举动都会立即终止游戏。在第二种配置中,使用了困难的规则,游戏具有更大的状态空间。每个fuzzer都经过了未经修改的版本以及与IJON组合的测试。每个实验进行一小时。
  • IJON:作者添加了一个简单的单行注释(list-10),该注释使用x和y坐标的组合作为反馈。游戏中任何新访问的位置都被视为新的覆盖范围。

如表I所示,没有一种原始工具能够解决迷宫的更复杂变体。但是,有了一个单行注解和IJON的支持,所有的fuzzer都可以解决所有迷宫变体。表II显示了不同的模糊器解决迷宫变化所花费的时间。

使用IJON注释,AFL在简易迷宫中的性能与KLEE相当。在更困难的版本上,带有IJON的AFL比在简单版本上的KLEE还要快而KLEE无法解决难题。此外,虽然没有IJON的AFL有时可以在简易模式下解决小迷宫,但结合IJON的AFL比没有IJON的AFL快20倍以上。最后,与baseline AFL相比,AFL的大多数扩展(例如LAF-INTEL,QSYM和ANGORA)实际上反而降低了性能。这是由于这些fuzzer提供的附加机制所致,在这种情况下,该机制产生了额外的成本,但没有提供任何价值。

5.3 Super Mario Bros. – Large Known State Values

作者通过从stdin读取所有键盘命令的方式,并对游戏进行了修改,使游戏角色Mario始终向右跑且停止移动时会被杀死。该修改是为了在较短的执行时间下产生结果。作者将AFL和AFL与IJON注释结合使用来玩游戏并观察游戏的进度。

在没有注释的情况下运行了AFL 12小时,并观察了Mario可以达到的不同关卡。作者添加了一个使用ijon_max原语的简单注释,并为每个可能的高度(以图块为单位)创建一个slot。在每个slot中,最大化玩家的x坐标。通过这一简单的单行注释(list-9),fuzzer可以在几分钟内解决几乎所有级别。实际上,使用IJON的AFL可以解决除3个级别之外的所有级别。应该注意的是,这些级别(级别6-2)中只有一个可以解决。它仍未解决是因为fuzzing固有的随机性。常规AFL表现很挣扎:花费更长的时间,解决的关卡少得多,而未解决的关卡的进度也更少。

5.4 Structured Input Formats – State Change Logging

为了演示使用状态更改日志记录探索结构化输入格式的能力,作者在三个示例中对带有IJON的AFL进行了评估。前两个摘自AFLSMART的评估,在他们的论文中提出多个案例研究,并对libpng和WavPack进行了详细的讨论。作者选择了两个示例,并添加了一个简单的state_change_log注释。

作者还提供了随机wav/png文件的前1024个字节作为种子,并使用了源代码中的字符串字典。通过这个简单的注释,能够发现两个目标中的错误,而AFL找不到任何错误。此外,作者在libpng中发现了另一个out-of-bounds read,在最新版本的libpng中仍然存在(但已报告)。这表明对于这些结构化的输入格式,发现这些安全问题实际上并不需要语法。在解析器代码中添加一个小的注释有时可以为分析人员提供完全相同的好处,而成本却要低得多。

为了进一步证实这一发现,作者选择了另一个示例来演示具有相同state_change_log注释的AFL如何探索比普通AFL更大的功能组合。 LIBTPMS是用于可信平台模块(TPM)安全协处理器的软件仿真器。它通常用于虚拟化解决方案中以创建虚拟化模块。 TPM指定了可用于与协处理器进行交互的协议。该协议由各种可能的消息组成,每个消息都有一个特定的解析器。在这个实验中作者并不关心解决单个约束,而是关注探索的不同消息序列的数量,所以只评估AFL和带有IJON的AFL。 a)初始运行:运行AFL 24小时,并绘制找到的路径的子集。为了可视化,我们还包含了一次运行中发现的所有消息的图表。每个节点都是已成功解析和处理的消息。如果成功地彼此处理消息A和消息B,则在节点A和B之间添加一条边。 b)IJON:添加了一个基于更改日志的注释,该注释存储了最近4个成功解析和处理的消息ID。然后使用这些ID来创建其他反馈。注释的代码显示在list-7中。使用此注释,又进行了24小时的运行。可以在表IV的“ IJON”列中找到发现的路径数。显然,IJON导致交互更加复杂。

从AFLSMART示例和TPM的实验中,可以看出,使用状态更改日志注释可以极大地提高探索不同消息链的能力,而这是当前技术无法涵盖的。尤其是,在libpng和WavPack上进行的实验表明,这个以前未开发的空间包含一些关键错误,之前的方法很难找到。

5.5 Binutils Hash map - Missing Intermediate States

作者从前面介绍的哈希图示例中提取了相关的代码(list-3)。为了提供IJON注释,作者在哈希图中使用了可用的迭代函数。对于哈希图中的每个条目,执行一个IJON字符串比较。list-11中可以看到修改后的结果。哈希映射查找是一对多的字符串比较,作者在查找函数中创建一个字符串比较操作的显式循环,然后尝试在所有比较中最大化匹配字节的最大数量。

作者使用带有和不带有IJON自定义注释的各种fuzzer进行了实验。结果显示在表V和表VI中。 a)初始运行:在初始运行期间,实验中的任何fuzzer都无法解决未修改形式的约束。 b)IJON:用一个小的注释扩展了目标(三行,如上所述)。应用此注释后,所有的fuzzer都可以在几分钟内解决约束,只有AFLFAST在三分之二的测试中失败了。但是其唯一成功的那次在6分钟内解决了约束。

5.6 Cyber Grand Challenge

为了进一步证实fuzzing目标有助于发现安全问题,作者在DARPA CGC数据集的Linux端口上进行了实验。作者选择了30个CGC目标的随机子集。对于其中的八个目标,即使用提供的PoV也不会导致崩溃。作者手动检查了剩下的22个挑战,并使用AFL和IJON进行了fuzz,设法在10个目标中产生了崩溃,结果显示在表VII中。

由于多种原因,无法使剩余的12个目标崩溃:三个目标仅包含不会导致崩溃的信息泄漏漏洞,因此它们这项工作的范围之内。其中两个目标包含的崩溃过于复杂而无法发现(需要触发一系列不同的错误才能导致可观察到的崩溃)。尽管可以设计出触发这些崩溃的IJON注释,但作者认为如果没有对bug的事先了解难以做到这一点。因此将这两个目标视为失败。在其余的7个目标中,有2个在作者的覆盖跟踪程序中几乎所有输入崩溃,其中3个需要非常大的输入,而AFL无法有效生成这些输入,另外2个运行非常缓慢,或者导致除少数输入之外的所有输入都超时。

5.7 Real-World Software

除了在状态日志实验中发现的内存损坏以及在CGC数据集中发现的新颖崩溃之外,作者还对其他软件进行了实验,以进一步证明IJON对于发现安全性漏洞有帮助。 特别是,我们选择了dmg2img,这个工具最近被WEIZZ的作者fuzz过。作者为发现的漏洞应用了补丁,并继续使用IJON进行fuzz,结果在dmg2img中发现了另外三个内存损坏漏洞,其中两个是WEIZZ发现的错误的变体,需要满足其他约束条件才能实现的危险字符串操作函数的不安全使用。 第三个错误是整数溢出,导致大小为零的分配。 稍后,将偏移量减一的字节设置为零,从而破坏了malloc元数据,如list-12所示。

6 Discussion and Conclusion

在本文中,作者描述了一种和fuzzer的交互式方法。尽管这提供了指导fuzzing的新方法,但它需要手动检查测试范围、了解约束难以解决的原因并手动创建合适的注释。手动操作会给分析人员带来一定的成本,但该方法可以克服当前fuzzing的一些障碍。

当前在IJON中使用的注释是手动添加的,未来或许可以设计针对单个的困难约束条件自动推断添加注释的方法。(我觉得不大可行) 一些现有的fuzzing方法使用了作者描述的注释的子集。例如,可以通过在比较中注释相等的字节数来表示LAF-INTEL。同样,ANGORA已经为所有覆盖范围实现了基于调用堆栈的虚拟状态。但是,所有这些工具都会不加选择地使用其他反馈。因此,它们仅限于低信息增益反馈机制。以类似的方式使用更精确的反馈将导致更大的队列并降低性能。 最后,现在的注释需要有权限访问源码,作者认为未来是可以拓展到二进制文件中的。

实际上,分析人员通常习惯于手动选择解决覆盖率的种子文件或设计解决约束的自定义mutator(例如,通过提供字典或语法),又或者是尝试手动删除诸如校验和之类的困难约束。

作者实际上引入了“注释机制”来为分析人员提供与fuzzer更有效的交互方式,通过简单的几行注释就能解决现有fuzzer无法解决的困难问题。