最初由 Michal Zalewski lcamt[email protected] 开发。
如果您没有时间阅读此文件,请参阅 QuickStartGuide.txt。
模糊测试是识别现实软件中安全问题的最强大且经过验证的策略之一;它是迄今为止在安全关键软件中发现的绝大多数远程代码执行和权限升级错误的罪魁祸首。
不幸的是,模糊测试也相对较浅。盲目的随机突变使其不太可能到达测试代码中的某些代码路径,从而使某些漏洞完全超出了该技术的范围。
已经有许多尝试来解决这个问题。由 Tavis Ormandy 首创的早期方法之一是语料库蒸馏。该方法依靠覆盖信号从大量高质量的候选文件语料库中选择感兴趣的种子子集,然后通过传统方式对它们进行模糊测试。该方法效果非常好,但需要这样的语料库随时可用。此外,块覆盖率测量仅提供对程序状态的非常简单的理解,并且对于指导长期的模糊测试工作不太有用。
其他更复杂的研究集中在程序流分析(“concolic执行”)、符号执行或静态分析等技术上。所有这些方法在实验环境中都非常有前途,但在实际使用中往往会遇到可靠性和性能问题——并且目前还没有提供“哑”模糊技术的可行替代方案。
American Fuzzy Lop 是一种强力模糊器,结合了极其简单但坚如磐石的仪器引导遗传算法。它使用一种修改形式的边缘覆盖来轻松地获取程序控制流的细微的、局部范围的变化。
稍微简化一下,整体算法可以总结为:
将用户提供的初始测试用例加载到队列中,
从队列中取出下一个输入文件,
尝试将测试用例修剪到不会改变程序的测量行为的最小尺寸,
使用平衡且经过充分研究的各种传统模糊测试策略反复变异文件,
如果任何生成的突变导致仪器记录新的状态转换,请将突变输出添加为队列中的新条目。
转到2。
发现的测试用例也会定期剔除,以消除那些被更新、覆盖率更高的发现所淘汰的测试用例;并经历其他几个仪器驱动的工作最小化步骤。
作为模糊测试过程的副作用,该工具创建了一个小型的、独立的有趣测试用例语料库。这些对于播种其他劳动或资源密集型测试制度非常有用 - 例如,用于压力测试浏览器、办公应用程序、图形套件或闭源工具。
该模糊器经过彻底测试,其开箱即用的性能远远优于盲目模糊测试或仅覆盖工具。
当源代码可用时,可以通过配套工具注入检测,该工具可以在第三方代码的任何标准构建过程中作为 gcc 或 clang 的直接替代品。
该仪器对性能的影响相当有限;与 afl-fuzz 实现的其他优化相结合,大多数程序都可以比传统工具更快甚至更快地进行模糊测试。
重新编译目标程序的正确方法可能会根据构建过程的具体情况而有所不同,但几乎通用的方法是:
$ CC=/path/to/afl/afl-gcc ./configure
$ make clean all
对于 C++ 程序,您还需要设置CXX=/path/to/afl/afl-g++
。
clang 包装器(afl-clang 和 afl-clang++)可以以相同的方式使用; clang 用户还可以选择利用更高性能的检测模式,如 llvm_mode/README.llvm 中所述。
测试库时,您需要找到或编写一个简单的程序,从标准输入或文件中读取数据并将其传递到测试的库。在这种情况下,必须将此可执行文件链接到检测库的静态版本,或者确保在运行时加载正确的 .so 文件(通常通过设置LD_LIBRARY_PATH
)。最简单的选项是静态构建,通常可以通过以下方式实现:
$ CC=/path/to/afl/afl-gcc ./configure --disable-shared
调用“make”时设置AFL_HARDEN=1
将导致 CC 包装器自动启用代码强化选项,从而更容易检测简单的内存错误。 Libdislocator,AFL 中包含的一个辅助库(请参阅 libdislocator/README.dislocator)也可以帮助发现堆损坏问题。
附言。建议 ASAN 用户查看notes_for_asan.txt 文件以了解重要注意事项。
当源代码不可用时,模糊器会为黑盒二进制文件的快速、动态检测提供实验支持。这是通过在鲜为人知的“用户空间模拟”模式下运行的 QEMU 版本来完成的。
QEMU 是一个独立于 AFL 的项目,但您可以通过执行以下操作来方便地构建该功能:
$ cd qemu_mode
$ ./build_qemu_support.sh
有关其他说明和注意事项,请参阅 qemu_mode/README.qemu。
该模式比编译时检测慢大约 2-5 倍,不太利于并行化,并且可能有一些其他怪癖。
为了正确运行,模糊器需要一个或多个起始文件,其中包含目标应用程序通常期望的输入数据的良好示例。有两个基本规则:
保持文件小。尽管不是绝对必要的,但低于 1 kB 是理想的。有关大小为何重要的讨论,请参阅 perf_tips.txt。
仅当多个测试用例在功能上彼此不同时才使用它们。使用五十张不同的度假照片来模糊图像库是没有意义的。
您可以在该工具附带的 testcases/ 子目录中找到许多启动文件的好示例。
附言。如果有大量数据可供筛选,您可能需要使用 afl-cmin 实用程序来识别功能不同的文件的子集,这些文件在目标二进制文件中执行不同的代码路径。
模糊测试过程本身由 afl-fuzz 实用程序执行。该程序需要一个包含初始测试用例的只读目录、一个存储其结果的单独位置以及要测试的二进制文件的路径。
对于直接从 stdin 接受输入的目标二进制文件,通常的语法是:
$ ./afl-fuzz -i testcase_dir -o findings_dir /path/to/program [...params...]
对于从文件获取输入的程序,请使用“@@”来标记目标命令行中应放置输入文件名的位置。模糊器将代替你:
$ ./afl-fuzz -i testcase_dir -o findings_dir /path/to/program @@
您还可以使用 -f 选项将变异数据写入特定文件。如果程序需要特定的文件扩展名等,这很有用。
未检测的二进制文件可以在 QEMU 模式(在命令行中添加 -Q)或传统的盲模糊器模式(指定 -n)中进行模糊测试。
您可以使用 -t 和 -m 覆盖执行进程的默认超时和内存限制;可能需要触及这些设置的目标的罕见示例包括编译器和视频解码器。
perf_tips.txt 中讨论了优化模糊测试性能的技巧。
请注意,afl-fuzz 首先执行一系列确定性模糊测试步骤,这可能需要几天时间,但往往会产生简洁的测试用例。如果您想要立即获得快速而肮脏的结果 - 类似于 zzuf 和其他传统模糊器 - 将 -d 选项添加到命令行。
有关如何解释显示的统计信息和监视进程运行状况的信息,请参阅 status_screen.txt 文件。请务必查阅此文件,尤其是当任何 UI 元素以红色突出显示时。
模糊测试过程将继续,直到您按下 Ctrl-C。至少,您希望允许模糊器完成一个队列周期,这可能需要几个小时到一周左右的时间。
输出目录中创建了三个子目录并实时更新:
queue/ - 每个独特执行路径的测试用例,以及用户给出的所有起始文件。这是第 2 节中提到的合成语料库。在将此语料库用于任何其他目的之前,您可以使用 afl-cmin 工具将其缩小到较小的大小。该工具将找到提供同等边缘覆盖范围的较小文件子集。
crashes/ - 导致被测试程序接收致命信号的独特测试用例(例如,SIGSEGV、SIGILL、SIGABRT)。这些条目按接收到的信号进行分组。
hangs/ - 导致测试程序超时的独特测试用例。某些内容被分类为挂起之前的默认时间限制是 1 秒和 -t 参数的值中的较大者。该值可以通过设置 AFL_HANG_TMOUT 进行微调,但这很少有必要。
如果关联的执行路径涉及先前记录的故障中未出现的任何状态转换,则崩溃和挂起被认为是“唯一的”。如果可以通过多种方式到达单个错误,则在此过程的早期会出现一些计数膨胀,但这应该很快就会逐渐减少。
崩溃和挂起的文件名与父级、无故障队列条目相关。这应该有助于调试。
当您无法重现 afl-fuzz 发现的崩溃时,最可能的原因是您没有设置与该工具使用的内存限制相同的内存限制。尝试:
$ LIMIT_MB=50
$ ( ulimit -Sv $[LIMIT_MB << 10] ; /path/to/tested_binary ... )
更改 LIMIT_MB 以匹配传递给 afl-fuzz 的 -m 参数。在 OpenBSD 上,还将 -Sv 更改为 -Sd。
任何现有的输出目录也可用于恢复中止的作业;尝试:
$ ./afl-fuzz -i- -o existing_output_dir [...etc...]
如果安装了 gnuplot,您还可以使用 afl-plot 为任何活动的模糊测试任务生成一些漂亮的图表。有关其外观的示例,请参阅 http://lcamtuf.coredump.cx/afl/plot/。
afl-fuzz 的每个实例大约占用一个核心。这意味着在多核系统上,需要并行化才能充分利用硬件。有关如何在多核或多台联网机器上模糊公共目标的提示,请参阅parallel_fuzzing.txt。
并行模糊测试模式还提供了一种将 AFL 与其他模糊器、符号或 concolic 执行引擎等连接的简单方法;再次,请参阅parallel_fuzzing.txt 的最后一部分以获取提示。
默认情况下,afl-fuzz 变异引擎针对紧凑数据格式进行了优化 - 例如图像、多媒体、压缩数据、正则表达式语法或 shell 脚本。它不太适合那些特别冗长和多余的语言——特别是 HTML、SQL 或 JavaScript。
为了避免构建语法感知工具的麻烦,afl-fuzz 提供了一种方法,通过可选的语言关键字字典、魔术头或与目标数据类型相关的其他特殊标记来为模糊测试过程提供种子,并使用它来重建随时随地的底层语法:
http://lcamtuf.blogspot.com/2015/01/afl-fuzz-making-up-grammar-with.html
要使用此功能,您首先需要以 dictionaries/README.dictionaries 中讨论的两种格式之一创建字典;然后通过命令行中的 -x 选项将模糊器指向它。
(该子目录中也已经提供了几个常用词典。)
无法提供底层语法的更结构化的描述,但模糊器可能会仅根据仪器反馈来找出其中的一些内容。这在实践中确实有效,例如:
http://lcamtuf.blogspot.com/2015/04/finding-bugs-in-sqlite-easy-way.html
附言。即使没有给出明确的字典,afl-fuzz 也会在确定性字节翻转期间通过密切观察检测来尝试提取输入语料库中现有的语法标记。这适用于某些类型的解析器和语法,但不如 -x 模式好。
如果字典确实很难获得,另一种选择是让 AFL 运行一段时间,然后使用 AFL 附带的令牌捕获库。为此,请参阅 libtokencap/README.tokencap。
基于覆盖范围的崩溃分组通常会生成一个小数据集,可以手动或使用非常简单的 GDB 或 Valgrind 脚本快速分类。每个崩溃还可以追溯到队列中其父级非崩溃测试用例,从而更容易诊断故障。
话虽如此,重要的是要承认,如果不进行大量调试和代码分析工作,一些模糊测试崩溃可能很难快速评估其可利用性。为了协助完成此任务,afl-fuzz 支持使用 -C 标志启用的非常独特的“崩溃探索”模式。
在这种模式下,模糊器将一个或多个崩溃测试用例作为输入,并使用其反馈驱动的模糊策略非常快速地枚举程序中可以到达的所有代码路径,同时保持程序处于崩溃状态。
不会导致崩溃的突变会被拒绝;任何不影响执行路径的更改也是如此。
输出是一个小的文件语料库,可以非常快速地检查它,以了解攻击者对错误地址的控制程度,或者是否有可能通过初始越界读取,并查看其背后的内容。
哦,还有一件事:为了最小化测试用例,请尝试 afl-tmin。该工具的操作方式非常简单:
$ ./afl-tmin -i test_case -o minimized_result -- /path/to/program [...]
该工具适用于崩溃和非崩溃测试用例。在崩溃模式下,它会很乐意接受已检测和未检测的二进制文件。在非崩溃模式下,最小化器依靠标准 AFL 工具来使文件更简单,而不改变执行路径。
最小化器以与 afl-fuzz 兼容的方式接受 -m、-t、-f 和 @@ 语法。
AFL 最近添加的另一个工具是 afl-analyze 工具。它需要一个输入文件,尝试顺序翻转字节,并观察被测试程序的行为。然后,它根据哪些部分显得重要、哪些部分不重要,对输入进行颜色编码;虽然不是万无一失的,但它通常可以提供对复杂文件格式的快速洞察。有关其操作的更多信息可以在 Technical_details.txt 末尾附近找到。
模糊测试也是一种奇妙但未被充分利用的技术,用于发现非崩溃的设计和实现错误。通过修改目标程序以在以下情况下调用 abort() ,发现了相当多有趣的错误:
当给定相同的模糊器生成的输入时,两个 bignum 库会产生不同的输出,
当要求图像库连续多次解码相同的输入图像时,图像库会产生不同的输出,
当迭代序列化和反序列化模糊器提供的数据时,序列化/反序列化库无法产生稳定的输出,
当要求压缩然后解压缩特定 blob 时,压缩库会生成与输入文件不一致的输出。
实施这些或类似的健全性检查通常只需要很少的时间;如果您是特定包的维护者,则可以使用#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
(也与 libfuzzer 共享的标志)或#ifdef __AFL_COMPILER
(这仅适用于 AFL)将此代码设为条件。
请记住,与许多其他计算密集型任务类似,模糊测试可能会给您的硬件和操作系统带来压力。尤其:
您的 CPU 运行时会很热,需要足够的冷却。在大多数情况下,如果冷却不足或停止正常工作,CPU 速度将自动降低。也就是说,特别是在不太合适的硬件(笔记本电脑、智能手机等)上进行模糊测试时,某些东西爆炸并非完全不可能。
目标程序最终可能会不稳定地占用千兆字节的内存或用垃圾文件填充磁盘空间。 AFL 试图强制实施基本的内存限制,但无法防止每一个可能的事故。最重要的是,您不应该对数据丢失风险不可接受的系统进行模糊测试。
模糊测试涉及对文件系统进行数十亿次读取和写入。在现代系统上,这通常会被大量缓存,从而导致相当适度的“物理”I/O - 但有许多因素可能会改变这个等式。您有责任监控潜在的问题;如果 I/O 非常繁重,许多 HDD 和 SSD 的使用寿命可能会缩短。
在 Linux 上监视磁盘 I/O 的一个好方法是“iostat”命令:
$ iostat -d 3 -x -k [...optional disk ID...]
以下是 AFL 的一些最重要的注意事项:
AFL 通过检查第一个生成的进程是否因信号(SIGSEGV、SIGABRT 等)而死亡来检测故障。为这些信号安装自定义处理程序的程序可能需要注释掉相关代码。同样,由模糊目标产生的子进程中的错误可能会逃避检测,除非您手动添加一些代码来捕获该错误。
与任何其他暴力工具一样,如果使用加密、校验和、加密签名或压缩来完全包装要测试的实际数据格式,则模糊器提供的覆盖范围有限。
要解决此问题,您可以注释掉相关检查(请参阅experimental/libpng_no_checksum/以获取灵感);如果这是不可能的,您还可以编写一个后处理器,如experimental/post_library/中所述。
ASAN 和 64 位二进制文件之间存在一些不幸的权衡。这并不是由于 afl-fuzz 的任何特定错误所致;而是由于 afl-fuzz 的任何特定错误所致。有关提示,请参阅notes_for_asan.txt。
不直接支持模糊网络服务、后台守护程序或需要 UI 交互才能工作的交互式应用程序。您可能需要进行简单的代码更改,以使它们以更传统的方式运行。 Preeny 也可能提供一个相对简单的选项 - 请参阅:https://github.com/zardus/preeny
还可以在以下位置找到修改基于网络的服务的一些有用提示:https://www.fastly.com/blog/how-to-fuzz-server-american-fuzzy-lop
AFL 不输出人类可读的报道数据。如果您想监控覆盖范围,请使用 Michael Rash 的 afl-cov:https://github.com/mrash/afl-cov
有时,有感知能力的机器会反抗它们的创造者。如果您遇到这种情况,请参阅http://lcamtuf.coredump.cx/prep/。
除此之外,请参阅安装以获取特定于平台的提示。
如果没有来自以下机构的反馈、错误报告或补丁,afl-fuzz 的许多改进都是不可能实现的:
Jann Horn Hanno Boeck
Felix Groebert Jakub Wilk
Richard W. M. Jones Alexander Cherepanov
Tom Ritter Hovik Manucharyan
Sebastian Roschke Eberhard Mattes
Padraig Brady Ben Laurie
@dronesec Luca Barbato
Tobias Ospelt Thomas Jarosch
Martin Carpenter Mudge Zatko
Joe Zbiciak Ryan Govostes
Michael Rash William Robinet
Jonathan Gray Filipe Cabecinhas
Nico Weber Jodie Cunningham
Andrew Griffiths Parker Thompson
Jonathan Neuschfer Tyler Nighswander
Ben Nagy Samir Aguiar
Aidan Thornton Aleksandar Nikolich
Sam Hakim Laszlo Szekeres
David A. Wheeler Turo Lamminen
Andreas Stieger Richard Godbee
Louis Dassy teor2345
Alex Moneger Dmitry Vyukov
Keegan McAllister Kostya Serebryany
Richo Healey Martijn Bogaard
rc0r Jonathan Foote
Christian Holler Dominique Pelle
Jacek Wielemborek Leo Barnes
Jeremy Barnes Jeff Trull
Guillaume Endignoux ilovezfs
Daniel Godas-Lopez Franjo Ivancic
Austin Seipp Daniel Komaromy
Daniel Binderman Jonathan Metzman
Vegard Nossum Jan Kneschke
Kurt Roeckx Marcel Bohme
Van-Thuan Pham Abhik Roychoudhury
Joshua J. Drake Toby Hutton
Rene Freingruber Sergey Davidoff
Sami Liedes Craig Young
Andrzej Jackowski Daniel Hodson
谢谢你!
问题?担忧?错误报告?请使用 GitHub。
该项目还有一个邮件列表;要加入,请发送邮件至 [email protected]。或者,如果您想先浏览档案,请尝试:https://groups.google.com/group/afl-users。