AFL 用户指南

了解状态屏幕

本文档概述了状态屏幕 - 以及对 UI 中显示的任何警告和红色文本进行故障排除的提示。

0) 关于颜色的说明

状态屏幕和错误消息使用颜色来保持可读性,并将您的注意力吸引到最重要的细节上。例如,红色几乎总是意味着“查阅此文档”:-)

不幸的是,只有当您的终端使用传统的 un*x 调色板(黑色背景上的白色文本)或类似的调色板时,UI 才会正确呈现。

如果您使用的是反向视频,您可能需要更改设置,例如:

  • 对于 GNOME 终端,转到编辑 > 配置文件首选项,选择“颜色”选项卡,然后从内置方案列表中选择“黑底白字”。
  • 对于 MacOS X Terminal 应用程序,通过 Shell > New Window 菜单使用“Pro”方案打开一个新窗口(或将“Pro”设为默认值)。

或者,如果您真的喜欢您当前的颜色,您可以编辑 config.h 以注释掉 USE_COLORS,然后执行“make clean all”。

我不知道有任何其他简单的方法可以在不引起其他副作用的情况下完成这项工作 - 对此感到抱歉。

顺便说一句,让我们来谈谈屏幕上的实际情况……

1) 处理时间

+----------------------------------------------------+
|        run time : 0 days, 8 hrs, 32 min, 43 sec    |
|   last new path : 0 days, 0 hrs, 6 min, 40 sec     |
| last uniq crash : none seen yet                    |
|  last uniq hang : 0 days, 1 hrs, 24 min, 32 sec    |
+----------------------------------------------------+

这部分是相当不言自明的:它告诉你模糊器运行了多长时间以及自最近一次发现以来已经过去了多长时间。这被分解为“路径”(触发新执行模式的测试用例的简写)、崩溃和挂起。

在时间方面:没有硬性规定,但大多数 fuzzing 作业应该运行数天或数周;事实上,对于一个中等复杂的项目,第一次通过可能需要一天左右的时间。时不时地,一些工作将被允许运行数月。

有一件重要的事情需要注意:如果该工具在启动后几分钟内没有找到新路径,那么您可能没有正确调用目标二进制文件,并且它永远无法解析我们向其抛出的输入文件;另一种可能的解释是默认内存限制(-m)过于严格,并且程序在很早就未能分配缓冲区后退出;或者输入文件显然是无效的,并且总是无法通过基本的标题检查。

如果一段时间内没有出现新路径,您最终也会在本节中看到一个大红色警告 :-)

2) 总体结果

+-----------------------+
|  cycles done : 0      |
|  total paths : 2095   |
| uniq crashes : 0      |
|   uniq hangs : 19     |
+-----------------------+

本节中的第一个字段为您提供到目前为止完成的队列通过次数 - 即,模糊器检查迄今为止发现的所有有趣测试​​用例、模糊测试并循环回到最开始的次数。每个 fuzzing 会话应该被允许完成至少一个循环;理想情况下,应该运行比这更长的时间。

如前所述,第一次通过可能需要一天或更长时间,所以请坐下来放松一下。如果您想立即获得更广泛但更浅的覆盖范围,请尝试 -d 选项 - 它通过跳过确定性模糊测试步骤为您提供更熟悉的体验。然而,它在一些微妙的方面不如标准模式。

为了帮助确定何时按下 Ctrl-C,循环计数器采用了颜色编码。它在第一次通过时显示为洋红色,如果在随后的轮次中仍有新发现,则变为黄色,然后在结束时变为蓝色 - 最后,在模糊器较长时间没有看到任何动作后变为绿色。

屏幕这部分的剩余字段应该非常明显:到目前为止发现的测试用例(“路径”)的数量,以及唯一错误的数量。可以通过浏览输出目录来实时探索测试用例、崩溃和挂起,如解释输出中所述。

3) 循环进度

+-------------------------------------+
|  now processing : 1296 (61.86%)     |
| paths timed out : 0 (0.00%)         |
+-------------------------------------+

这个框告诉你模糊器在当前队列周期中的距离:它显示了它当前正在处理的测试用例的 ID,以及它决定放弃的输入数量,因为它们一直超时。

有时在第一行中显示的“*”后缀意味着当前处理的路径不是“首选”(稍后将在第 6 节中讨论的属性)。

如果您觉得 fuzzer 进展太慢,请参阅本文档第 2 节中有关 -d 选项的说明。

4) 地图覆盖率

+--------------------------------------+
|    map density : 10.15% / 29.07%     |
| count coverage : 4.03 bits/tuple     |
+--------------------------------------+

本节提供了一些关于嵌入在目标二进制文件中的检测所观察到的覆盖率的琐事。

框中的第一行告诉您我们已经命中了多少分支元组,与位图可以容纳的数量成正比。左边的数字描述了当前的输入;右边是整个输入语料库的值。

警惕极端情况:

  • 低于 200 左右的绝对数字表明以下三件事之一:程序非常简单;它没有正确检测(例如,由于链接到目标库的未检测副本);或者它在您的输入测试用例上过早地退出。fuzzer 会尝试用粉红色标记它,只是为了让你知道。

  • 对于大量使用模板生成代码的非常复杂的程序,很少会出现超过 70% 的百分比。

    因为高位图密度使模糊器更难可靠地识别新的程序状态,我建议使用 AFL_INST_RATIO=10 左右重新编译二进制文件并重试(参见 env_variables.txt)。

    模糊器将以红色标记高百分比。很有可能,除非您对极其复杂的软件(例如 v8、perl、ffmpeg)进行模糊测试,否则您永远不会看到这一点。

另一行处理二进制文件中元组命中计数的可变性。本质上,如果对于我们尝试过的所有输入,每个采用的分支总是采用固定次数,这将显示为“1.00”。当我们设法触发每个分支的其他命中计数时,指针将开始向“8.00”移动(8 位映射命中的每一位),但可能永远不会达到那个极端。

这些值一起用于比较依赖于相同检测二进制文件的几个不同模糊测试作业的覆盖率。

5) 阶段进度

+-------------------------------------+
|  now trying : interest 32/8         |
| stage execs : 3996/34.4k (11.62%)   |
| total execs : 27.4M                 |
|  exec speed : 891.7/sec             |
+-------------------------------------+

这部分让您深入了解模糊器现在实际在做什么。它告诉您当前阶段,可以是以下任何一个:

  • 校准 - 一个预模糊阶段,检查执行路径以检测异常,建立基线执行速度等。每当有新发现时执行非常短暂。
  • trim L/S - 另一个预模糊阶段,测试用例被修剪为仍然产生相同执行路径的最短形式。长度 (L) 和步距 (S) 的选择通常与文件大小有关。
  • bitflip L/S - 确定性位翻转。在任何给定时间都有 L 位切换,以 S 位增量遍历输入文件。当前的 L/S 型号有:1/1、2/1、4/1、8/8、16/8、32/8。
  • arith L/8 - 确定性算术。模糊器尝试将小整数减去或添加到 8、16 和 32 位值。步距始终为 8 位。
  • 兴趣 L/8 - 确定性值覆盖。fuzzer 有一个已知“有趣”的 8 位、16 位和 32 位值列表可供尝试。步距为 8 位。
  • extras - 字典术语的确定性注入。这可以显示为“用户”或“自动”,具体取决于模糊器是使用用户提供的字典(-x)还是自动创建的字典。您还将看到“over”或“insert”,具体取决于字典单词是覆盖现有数据还是通过偏移剩余数据以适应它们的长度来插入。
  • havoc - 一种带有堆叠随机调整的固定长度循环。在此阶段尝试的操作包括位翻转、用随机和“有趣的”整数覆盖、块删除、块复制以及各种与字典相关的操作(如果首先提供字典)。
  • splice - 在没有新路径的第一个完整队列周期之后启动的最后手段策略。它相当于“havoc”,除了它首先在任意选择的中点将队列中的两个随机输入拼接在一起。
  • sync - 仅在设置 -M 或 -S 时使用的阶段(请参阅并行模糊测试技巧)。不涉及真正的模糊测试,但该工具会扫描其他模糊测试器的输出并根据需要导入测试用例。第一次完成时,可能需要几分钟左右。

其余字段应该是不言而喻的:有当前阶段的执行计数进度指示器、全局执行计数器和当前程序执行速度的基准。这可能会从一个测试用例到另一个测试用例波动,但理想情况下,基准测试在大多数情况下应该超过 500 execs/sec - 如果它保持在 100 以下,那么这项工作可能需要很长时间。

模糊器也会明确警告您慢速目标。如果发生这种情况,请参阅性能提示以了解如何加快速度。

6) 深入调查

+--------------------------------------+
| favored paths : 879 (41.96%)         |
|  new edges on : 423 (20.19%)         |
| total crashes : 0 (0 unique)         |
|  total tmouts : 24 (19 unique)       |
+--------------------------------------+

这为您提供了几个主要对完全书呆子感兴趣的指标。该部分包括模糊器最喜欢的路径数量,这些路径基于嵌入到代码中的最小化算法(这些将获得更多的空中时间),以及实际上导致更好边缘覆盖的测试用例数量(而不是仅仅推动分支击中计数器)。还有其他更详细的崩溃和超时计数器。

请注意,超时计数器与挂起计数器有些不同;这包括所有超过超时的测试用例,即使它们没有超出足以被归类为挂起的余量。

7) 模糊测试策略收益

+-----------------------------------------------------+
|   bit flips : 57/289k, 18/289k, 18/288k             |
|  byte flips : 0/36.2k, 4/35.7k, 7/34.6k             |
| arithmetics : 53/2.54M, 0/537k, 0/55.2k             |
|  known ints : 8/322k, 12/1.32M, 10/1.70M            |
|  dictionary : 9/52k, 1/53k, 1/24k                   |
|       havoc : 1903/20.0M, 0/0                       |
|        trim : 20.31%/9201, 17.05%                   |
+-----------------------------------------------------+

这只是另一个针对书呆子的部分,用于跟踪我们为前面讨论的每种模糊测试策略已经获得了多少路径,与尝试的执行者数量成正比。这有助于令人信服地验证关于 afl-fuzz 采用的各种方法的有用性的假设。

本节中的修剪策略统计数据与其他部分略有不同。这一行中的第一个数字显示了从输入文件中删除的字节的比率;第二个对应于实现这一目标所需的高管人数。最后,第三个数字显示了字节的比例,尽管无法删除,但被认为没有效果,并被排除在一些更昂贵的确定性模糊测试步骤之外。

8) 路径几何

+---------------------+
|    levels : 5       |
|   pending : 1570    |
|  pend fav : 583     |
| own finds : 0       |
|  imported : 0       |
| stability : 100.00% |
+---------------------+

本节中的第一个字段跟踪通过引导模糊过程达到的路径深度。本质上:用户提供的初始测试用例被认为是“级别 1”。通过传统的模糊测试可以派生的测试用例被认为是“2级”;通过使用这些作为后续模糊测试轮次的输入而得出的那些是“级别 3”;等等。因此,最大深度是您从 afl-fuzz 采用的仪器引导方法中获得多少价值的粗略代表。

下一个字段显示尚未经过任何模糊测试的输入数量。对于模糊器真正想要在这个队列周期中获得的“偏好”条目也给出了相同的统计信息(非偏好条目可能需要等待几个周期才能获得机会)。

接下来,我们有在这个模糊测试部分找到的新路径的数量,并在进行并行模糊测试时从其他模糊器实例导入;以及相同输入在多大程度上有时会在测试的二进制文件中产生可变行为。

最后一点实际上相当有趣:它测量了观察到的痕迹的一致性。如果一个程序对于相同的输入数据总是表现相同,它将获得 100% 的分数。当值较低但仍显示为紫色时,模糊过程不太可能受到负面影响。如果它变成红色,您可能会遇到麻烦,因为 AFL 将难以区分调整输入文件的有意义和“幻影”效果。

现在,大多数目标只会获得 100% 的分数,但是当您看到较低的数字时,有几件事需要注意:

  • 在测试的二进制文件中使用未初始化的内存以及一些内在的熵源。对 AFL 无害,但可能表明存在安全漏洞。
  • 尝试操作持久性资源,例如遗留的临时文件或共享内存对象。这通常是无害的,但您可能需要仔细检查以确保该程序不会过早退出。磁盘空间、SHM 句柄或其他全局资源不足也会触发此问题。
  • 击中一些实际上设计为随机运行的功能。一般无害。例如,在对 sqlite 进行模糊测试时,输入类似 'select random();' 将触发变量执行路径。
  • 多个线程以半随机顺序一次执行。当“稳定性”指标保持在 90% 左右时,这是无害的,但如果不是,则可能会成为问题。这是尝试的方法:
    • 使用来自 llvm_mode/ 的 afl-clang-fast - 它使用不易出现并发问题的线程本地跟踪模型,
    • 查看目标是否可以在没有线程的情况下编译或运行。常见的 ./configure 选项包括 –without-threads、–disable-pthreads 或 –disable-openmp。
    • 将 pthreads 替换为 GNU Pth ( https://www.gnu.org/software/pth/ ),它允许您使用确定性调度程序。
  • 在持久模式下,“稳定性”指标的轻微下降可能是正常的,因为重新输入时并非所有代码的行为都相同;但主要的下降可能意味着 __AFL_LOOP() 中的代码在后续迭代中的行为不正确(例如,由于不完整的清理或状态的重新初始化)并且大部分模糊测试工作都白费了。

检测到变量行为的路径在 <out_dir>/queue/.state/variable_behavior/ 目录中标有匹配条目,因此您可以轻松查找它们。

9) CPU 负载

[cpu: 25%]

这个小部件显示了本地系统上明显的 CPU 利用率。它是通过获取处于“可运行”状态的进程数,然后将其与系统上的逻辑核心数进行比较来计算的。

如果该值显示为绿色,则您使用的 CPU 内核数少于系统上可用的 CPU 内核数,并且可能可以并行化以提高性能;有关如何做到这一点的提示,请参阅并行模糊测试提示

如果该值显示为红色,则您的 CPU可能被超额订阅,并且运行额外的 fuzzer 可能不会给您带来任何好处。

当然,这个基准非常简单;它会告诉您有多少进程准备好运行,但不会告诉您它们可能需要多少资源。它也不区分物理内核、逻辑内核和虚拟化 CPU;它们中的每一个的性能特征都会有很大的不同。

如果您想要更准确的测量,可以从命令行运行 afl-gotcpu 实用程序。

10) 附录:状态和绘图文件

对于无人值守的操作,一些关键的状态屏幕信息也可以在输出目录的 fuzzer_stats 文件中以机器可读的格式找到。这包括:

  • start_time - 表示 afl-fuzz 开始时间的 unix 时间
  • last_update - 此文件的最后一次更新对应的 unix 时间
  • fuzzer_pid - fuzzer 进程的 PID
  • Cycles_done - 到目前为止完成的队列周期
  • execs_done - 尝试的 execve() 调用次数
  • execs_per_sec - 当前每秒执行数
  • path_total - 队列中的条目总数
  • path_found - 通过本地模糊测试发现的条目数
  • path_imported - 从其他实例导入的条目数
  • max_depth - 生成的数据集中的级别数
  • cur_path - 当前处理的条目号
  • pending_favs - 仍在等待模糊测试的首选条目数
  • pending_total - 等待被模糊测试的所有条目的数量
  • 稳定性 - 表现一致的位图字节的百分比
  • variable_paths - 显示可变行为的测试用例数
  • unique_crashes - 记录的唯一崩溃次数
  • unique_hangs - 遇到的唯一挂起数

其中大部分直接映射到前面讨论的 UI 元素。

最重要的是,您还可以找到一个名为“plot_data”的条目,其中包含大多数这些字段的可绘制历史记录。如果您安装了 gnuplot,您可以使用随附的“afl-plot”工具将其变成一个不错的进度报告。

环境变量

本文档讨论了 American Fuzzy Lop 使用的环境变量来公开各种可能(很少)对高级用户或某些类型的自定义模糊设置有用的奇异函数。有关一般说明手册,请参阅自述文件。

1) afl-gcc、afl-clang 和 afl-as 的设置

因为它们不能直接接受命令行选项,所以编译时工具相当广泛地使用环境变量:

  • 调用下游编译器时,设置 AFL_HARDEN 会自动添加代码强化选项。这目前包括 -D_FORTIFY_SOURCE=2 和 -fstack-protector-all。该设置对于捕获非崩溃内存错误很有用,但代价是非常轻微(低于 5%)的性能损失。

  • 默认情况下,包装器附加 -O3 以优化构建。在极少数情况下,这会导致使用 -Werror 构建的程序出现问题,这仅仅是因为 -O3 可以进行更彻底的代码分析并发出额外的警告。要禁用优化,请设置 AFL_DONT_OPTIMIZE。

  • 设置 AFL_USE_ASAN 会自动启用 ASAN,前提是您的编译器支持它。请注意,使用 ASAN 进行模糊测试具有一定的挑战性 - 请参阅将 ASAN 与 AFL 结合使用

    (您也可以通过 AFL_USE_MSAN 启用 MSAN;ASAN 和 MSAN 具有相同的陷阱;这些模式是互斥的。UBSAN 和其他奇异的消毒剂尚未得到官方支持,但很容易手动操作。)

  • 设置 AFL_CC、AFL_CXX 和 AFL_AS 可让您使用备用下游编译工具,而不是 $PATH 中默认的“clang”、“gcc”或“as”二进制文件。

  • AFL_PATH 可用于将 afl-gcc 指向 afl-as 的备用位置。一种可能的用途是experimental/clang_asm_normalize/,它允许您在编译clang代码时通过将规范器插入链中来检测手写程序集。(GCC 没有等效的功能。)

  • 将 AFL_INST_RATIO 设置为 0 到 100% 之间的百分比控制检测每个分支的概率。在处理使输出位图饱和的异常复杂的程序时,这(很少)很有用。示例包括 v8、ffmpeg 和 perl。

    (如果发生这种情况,afl-fuzz 会通过以火红色显示“位图密度”字段来提前警告您。)

    将 AFL_INST_RATIO 设置为 0 是一个有效的选择。这将仅检测函数入口点之间的转换,而不是单个分支。

  • AFL_NO_BUILTIN 使编译器生成适合与 libtokencap.so 一起使用的代码(但可能比没有标志运行得慢一些)。

  • TMPDIR 被 afl-as 用于临时文件;如果未设置此变量,则该工具默认为 /tmp。

  • 设置 AFL_KEEP_ASSEMBLY 可防止 afl-as 删除检测的程序集文件。有助于解决问题或了解该工具的工作原理。要将它们放在可预测的位置,请尝试以下操作:

    mkdir assembly_here TMPDIR=$PWD/assembly_here AFL_KEEP_ASSEMBLY=1 全部清除

  • 设置 AFL_QUIET 将防止 afl-cc 和 afl-as 横幅在编译期间显示,以防您发现它们分散注意力。

2) afl-clang-fast 的设置

本机 LLVM 检测助手接受第 #1 节中讨论的设置子集,但以下内容除外:

  • AFL_AS,因为这个工具链不直接调用 GNU as。
  • TMPDIR 和 AFL_KEEP_ASSEMBLY,因为没有创建临时程序集文件。

请注意,AFL_INST_RATIO 的行为与 afl-gcc 的行为有些不同,因为函数不是无条件地检测的 - 所以低值将产生更显着的效果。对于此工具,0 不是有效选择。

3) afl-fuzz 设置

主要的 fuzzer 二进制文件接受几个选项来禁用一些健全性检查或更改该工具的一些更奇特的语义:

  • 设置 AFL_SKIP_CPUFREQ 会跳过对 CPU 扩展策略的检查。如果您不能更改默认值(例如,没有对系统的 root 访问权限)并且可以接受一些性能损失,这很有用。

  • 设置 AFL_NO_FORKSRV 会禁用 forkserver 优化,对每个测试输入恢复为 fork + execve() 调用。这在使用不守规矩的库时很有用,这些库在初始化时创建线程或做其他疯狂的事情(在仪器有机会运行之前)。

    请注意,此设置会禁止一些通常在启动 forkserver 时进行的用户友好型诊断,并导致性能显着下降。

  • AFL_EXIT_WHEN_DONE 导致 afl-fuzz 在所有现有路径都被模糊化并且一段时间内没有新发现时终止。这通常由 UI 中的循环计数器变为绿色来指示。对于某些类型的自动化作业可能很方便。

  • 设置 AFL_NO_AFFINITY 会禁止尝试绑定到 Linux 系统上的特定 CPU 内核。这会减慢速度,但可以让您运行更多的 afl-fuzz 实例,而不是谨慎(如果您真的想要)。

  • AFL_SKIP_CRASHES 导致 AFL 容忍输入队列中的崩溃文件。这可以帮助解决程序仅间歇性崩溃的罕见情况,但在正常操作条件下不建议这样做。

  • 设置 AFL_HANG_TMOUT 允许您指定不同的超时来确定特定测试用例是否是“挂起”。默认值为 1 秒或 -t 参数的值,以较大者为准。如果您非常担心缓慢的输入,或者您不希望 AFL 花费太多时间对这些内容进行分类而只是快速将所有超时放入该 bin 中,则调低该值可能很有用。

  • AFL_NO_ARITH 导致 AFL 跳过大多数确定性算术。这对于加速基于文本的文件格式的模糊测试很有用。

  • AFL_SHUFFLE_QUEUE 在启动时随机重新排序输入队列。一些用户要求非正统的并行模糊测试设置,但不建议这样做。

  • 在 afl-fuzz 之上开发自定义检测时,您可以使用 AFL_SKIP_BIN_CHECK 来禁止对非检测二进制文件和 shell 脚本的检查;和 AFL_DUMB_FORKSRV 与 -n 设置一起指示 afl-fuzz 仍然遵循 fork 服务器协议,而不期望返回任何检测数据。

  • 在 -M 或 -S 模式下运行时,设置 AFL_IMPORT_FIRST 会导致模糊器在执行任何其他操作之前从其他实例导入测试用例。这使得 UI 中的“自己的发现”计数器更加准确。除了反美学之外,其他方面应该没有太大变化。

  • 设置 AFL_POST_LIBRARY 允许您为变异文件配置后处理器 - 例如,修复校验和。有关更多信息,请参见实验/post_library/。

  • AFL_FAST_CAL 使校准阶段的速度提高了约 2.5 倍(尽管精度较低),这在针对慢速目标启动会话时会有所帮助。

  • 屏幕底部显示的 CPU 小部件相当简单,可能会过早地抱怨高负载,尤其是在内核数较低的系统上。为避免令人震惊的红色,您可以设置 AFL_NO_CPU_RED。

  • 在 QEMU 模式 (-Q) 中,将在 AFL_PATH 中搜索 afl-qemu-trace。

  • 设置 AFL_PRELOAD 会导致 AFL 为目标二进制文件设置 LD_PRELOAD,而不会中断 afl-fuzz 进程本身。除其他外,这对于引导 libdislocator.so 很有用。

  • 设置 AFL_NO_UI 完全禁止 UI,只是定期打印一些基本统计信息。当 afl-fuzz 的输出重定向到文件或管道时,也会自动触发此行为。

  • 如果你是 Jakub,你可能需要 AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES。其他不需要申请。

  • 仅用于基准测试:AFL_BENCH_JUST_ONE 导致模糊器在处理第一个队列条目后退出;并且 AFL_BENCH_UNTIL_CRASH 导致它在发现第一次崩溃后立即退出。

4) afl-qemu-trace 的设置

用于检测纯二进制代码的 QEMU 包装器支持多种设置:

  • 可以设置 AFL_INST_RATIO 以跳过一些基本块的检测,这在处理非常复杂的二进制文件时很有用。
  • 设置 AFL_INST_LIBS 会导致翻译器也检测任何动态链接库(特别是包括 glibc)中的代码。
  • 底层的 QEMU 二进制文件将识别任何标准的“用户空间仿真”变量(例如,QEMU_STACK_SIZE),但应该没有理由接触它们。

5) afl-cmin 的设置

语料库最小化脚本提供很少的定制:

  • 设置 AFL_PATH 提供了一种指定 afl-showmap 和 afl-qemu-trace 位置的方法(后者仅在 -Q 模式下)。
  • AFL_KEEP_TRACES 使工具保留用于最小化的痕迹和其他元数据,并且通常在退出时删除。这些文件可以在 <out_dir>/.traces/*中找到。
  • AFL_ALLOW_TMP 允许此脚本和其他一些脚本在 /tmp 中运行。这在具有恶意用户的多用户系统上是一个适度的安全风险,但在专用的模糊测试盒上应该是安全的。

6) afl-tmin 的设置

几乎没有什么可玩的。好吧,在 QEMU 模式下(-Q),AFL_PATH 会搜索 afl-qemu-trace。除此之外,如果无法在当前工作目录中创建临时文件,则可以使用 TMPDIR。

如果您希望 afl-tmin 在最小化崩溃时要求执行路径匹配,则可以指定 AFL_TMIN_EXACT。这将使最小化变得不那么有用,但可能会阻止工具在非常有问题的软件中从一个崩溃条件“跳转”到另一个崩溃条件。您可能希望将它与 -e 标志结合使用。

7) afl-analyze 设置

您可以设置 AFL_ANALYZE_HEX 以将文件偏移量打印为十六进制而不是十进制。

8) libdislocator.so 的设置

该库尊重三个环境变量:

  • AFL_LD_LIMIT_MB 限制库允许的最大堆使用大小,以兆字节为单位。默认值为 1 GB。一旦超过此值,分配将返回 NULL。
  • AFL_LD_HARD_FAIL 通过在过度分配上调用 abort() 来改变行为,从而导致 AFL 将其视为崩溃。对于应该保持特定内存占用的程序很有用。
  • AFL_LD_VERBOSE 使库输出一些诊断消息,这些消息可能有助于查明任何观察到的问题的原因。
  • AFL_LD_NO_CALLOC_OVER 在 calloc() 溢出时禁止 abort()。大多数常见的分配器会在内部检查并返回 NULL,因此只有在更奇特的设置中才有安全风险。

9) libtokencap.so 的设置

该库接受 AFL_TOKEN_FILE 来指示应写入已发现令牌的位置。

10) afl-fuzz 和其他工具设置的第三方变量

afl-fuzz 不直接解释几个变量,但如果环境中不存在,则将其设置为最佳值:

  • 默认情况下,LD_BIND_NOW 设置为通过强制链接器在 fork 服务器启动之前完成所有工作来加速模糊测试。您可以通过预先设置 LD_BIND_LAZY 来覆盖它,但这几乎可以肯定毫无意义。

  • 默认情况下,ASAN_OPTIONS 设置为:

    abort_on_error=1 detect_leaks=0 symbolize=0 allocator_may_return_null=1

    如果您想设置自己的选项,请确保包含 abort_on_error=1 - 否则,fuzzer 将无法检测到测试应用程序中的崩溃。类似地,包括 symbolize=0,因为没有它,AFL 可能难以分辨崩溃和挂起。

  • 同样,默认情况下,MSAN_OPTIONS 设置为:

    exit_code=86(由于遗留原因需要) abort_on_error=1 symbolize=0 msan_track_origins=0 allocator_may_return_null=1

    自定义任何东西时一定要包含第一个,因为某些 MSAN 版本在出错时不会调用 abort(),我们需要一种检测故障的方法。

并行模糊测试技巧

本文档讨论了在单台机器上或跨系统队列同步 afl-fuzz 作业。有关一般说明手册,请参阅自述文件。

1) 简介

afl-fuzz 的每个副本都将占用一个 CPU 内核。这意味着在一个 n 核系统上,您几乎总是可以运行 n 个并发的模糊测试作业,而几乎不会影响性能(您可以使用 afl-gotcpu 工具来确保)。

事实上,如果您只依赖多核系统上的一项工作,您将无法充分利用硬件。因此,并行化通常是正确的方法。

当针对多个不相关的二进制文件或在“哑”(-n)模式下使用该工具时,只需启动几个完全独立的 afl-fuzz 实例就可以了。当您想让多个 fuzzer 打击一个共同目标时,情况会变得更加复杂:如果一个 fuzzer 合成了一个难以命中但有趣的测试用例,其余的实例将无法使用该输入来指导他们的工作。

为了帮助解决这个问题,afl-fuzz 提供了一种简单的方法来动态同步测试用例。

2) 单系统并行化

如果您希望在本地系统上跨多个内核并行化单个作业,只需创建一个新的空输出目录(“sync dir”),该目录将由 afl-fuzz 的所有实例共享;然后为每个实例提出一个命名方案——比如“fuzzer01”、“fuzzer02”等。

像这样运行第一个(“master”,-M):

$ ./afl-fuzz -i testcase_dir -o sync_dir -M fuzzer01 [...other stuff...]

…然后,像这样启动辅助 (-S) 实例:

$ ./afl-fuzz -i testcase_dir -o sync_dir -S fuzzer02 [...other stuff...]
$ ./afl-fuzz -i testcase_dir -o sync_dir -S fuzzer03 [...other stuff...]

每个 fuzzer 都会将其状态保存在一个单独的子目录中,如下所示:

/path/to/sync_dir/fuzzer01/

每个实例还将定期重新扫描顶级同步目录以查找其他 fuzzer 发现的任何测试用例 - 当它们被认为足够有趣时,会将它们合并到自己的 fuzzing 中。

-M 和 -S 模式的区别在于主实例仍将执行确定性检查;而次要实例将直接进行随机调整。如果您根本不想进行确定性模糊测试,可以使用 -S 运行所有实例。对于非常缓慢或复杂的目标,或者在运行高度并行化的作业时,这通常是一个不错的计划。

请注意,运行多个 -M 实例是一种浪费,尽管有实验支持并行化确定性检查。为了利用它,您需要像这样创建 -M 实例:

$ ./afl-fuzz -i testcase_dir -o sync_dir -M masterA:1/3 [...]
$ ./afl-fuzz -i testcase_dir -o sync_dir -M masterB:2/3 [...]
$ ./afl-fuzz -i testcase_dir -o sync_dir -M masterC:3/3 [...]

…“:”之后的第一个值是特定主实例的顺序 ID(从 1 开始),第二个值是分配确定性模糊测试的模糊测试器总数。请注意,如果您启动的 fuzzer 少于传递给 -M 的第二个数字所指示的数量,则最终覆盖率可能会很差。

您还可以使用提供的 afl-whatsup 工具从命令行监控作业的进度。当实例不再寻找新路径时,可能是时候停止了。

警告:明确指定 -f 选项时要小心。每个 fuzzer 必须使用单独的临时文件;否则,事情就会向南发展。一个安全的例子可能是:

$ ./afl-fuzz [...] -S fuzzer10 -f file10.txt ./fuzzed/binary @@
$ ./afl-fuzz [...] -S fuzzer11 -f file11.txt ./fuzzed/binary @@
$ ./afl-fuzz [...] -S fuzzer12 -f file12.txt ./fuzzed/binary @@

如果您使用不带 -f 的 @@ 并让 afl-fuzz 提供文件名,这不是问题。

3) 多系统并行化

多系统并行化的基本操作原理与第 2 节中解释的机制类似。关键区别在于您需要编写一个执行两个操作的简单脚本:

  • 使用带授权密钥的 SSH 连接到每台机器,并为机器本地的每个 <fuzzer_id> 检索 /path/to/sync_dir/<fuzzer_id>/queue/ 目录的 tar 存档。最好使用在 fuzzer ID 中包含主机名的命名方案,这样您就可以执行以下操作:

    for s in {1..10}; do
      ssh user@host${s} "tar -czf - sync/host${s}_fuzzid*/[qf]*" >host${s}.tgz
    done
    
  • 在所有剩余的机器上分发和解包这些文件,例如:

    for s in {1..10}; do
      for d in {1..10}; do
        test "$s" = "$d" && continue
        ssh user@host${d} 'tar -kxzf -' <host${s}.tgz
      done
    done
    

在experimental/distributed_fuzzing/中有这样一个脚本的例子;您还可以在以下位置找到由 Martijn Bogaard 开发的更具特色的实验性工具:

Richo Healey 的另一个客户端-服务器实现是:

请注意,这些第三方工具在暴露于 Internet 或不受信任的用户的系统上运行是不安全的。

在开发自定义测试用例同步代码时,需要牢记几个优化:

  • 同步不必经常发生;每 30 分钟左右运行一次任务可能完全没问题。

  • 无需同步崩溃/或挂起/;您只需要复制 queue/* (理想情况下,还有 fuzzer_stats)。

  • 没有必要(也不建议!)覆盖现有文件;tar 中的 -k 选项是避免这种情况的好方法。

  • 没有必要为不在特定机器上本地运行的模糊器获取目录,并且在早期运行期间被简单地复制到该系统上。

  • 对于大型机群,您需要为每个主机整合 tarball,因为这将允许您使用 n 个 SSH 连接进行同步,而不是 n*(n-1)。

    您可能还想实现分阶段同步。例如,您可以有 10 组系统,其中第 1 组仅将测试用例推送到第 2 组;第 2 组只将他们推到第 3 组;依此类推,最后 10 组反馈给第 1 组。

    这种安排将允许测试有趣的案例在整个舰队中传播,而不必将每个模糊器队列复制到每个主机。

  • 您不希望在每个系统上都有一个 afl-fuzz 的“主”实例;您应该使用 -S 运行它们,并且只需在队列中的某处指定一个进程以使用 -M 运行。

不建议跳过同步脚本并直接在网络文件系统上运行模糊器I/O 等待状态下的意外延迟和无法杀死的进程可能会搞砸事情。

4) 远程监控和数据采集

您可以使用 screen、nohup、tmux 或类似的东西来运行 afl-fuzz 的远程实例。如果您将程序的输出重定向到文件,它会自动从精美的 UI 切换到更有限的状态报告。还有基本的机器可读信息总是写入输出目录中的 fuzzer_stats 文件。在本地,可以使用 afl-whatsup 解释该信息。

原则上,您可以使用 master (-M) 实例的状态屏幕来监控整体 fuzzing 进度并决定何时停止。在这种模式下,最重要的信号就是很长时间没有找到新的路径。如果您没有主实例,只需选择任何一个辅助实例即可观看并通过它。

您还可以依靠该实例的输出目录来收集合成的语料库,该语料库涵盖了在舰队中任何地方发现的所有值得注意的路径。辅助 (-S) 实例不需要任何特殊监控,只需确保它们已启动。

请记住,崩溃输入不会自动传播到主实例,因此您可能仍希望从同步或健康检查脚本中监视整个队列范围内的崩溃(请参阅 afl-whatsup)。

5) 不对称设置

可能值得注意的是,以下所有内容都是允许的:

  • 运行 afl-fuzz 与其他可以扩展覆盖范围的引导工具(例如,通过 concolic 执行)。第三方工具只需遵循上述协议即可从 out_dir/<fuzzer_id>/queue/*中提取新测试用例,并将自己的发现写入 out_dir/<ext_tool_id>/queue/* 中按顺序编号的 id:nnnnnn文件

  • 运行一些具有不同(但相关)目标二进制文件的同步模糊器。例如,同时对几个不同的 JPEG 解析器(例如,IJG jpeg 和 libjpeg-turbo)进行压力测试,同时共享发现的测试用例可以产生协同效应并提高整体覆盖率。

    (在这种情况下,每个二进制文件运行一个 -M 实例是一个不错的计划。)

  • 让一些模糊器以不同的方式调用二进制文件。例如,“djpeg”支持多种 DCT 模式,可通过命令行标志进行配置,而“dwebp”支持增量和一次性解码。在某些情况下,追求多种不同的模式,然后汇集测试用例将提高覆盖率。

  • 更不令人信服的是,使用不同的起始测试用例(例如,渐进式和标准 JPEG)或字典运行同步模糊器。同步机制确保测试集随着时间的推移变得相当均匀,但它引入了一些初始可变性。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值