新消息:如果你想尝试使用 TriforceAFL 和 TLSF,Richard Johnson 创建了一个 Dockerfile,可以同时安装两者(甚至为你构建一个 Linux 内核)。可在此处获取 https://hub.docker.com/r/moflow/afl-triforce/tags/。
另一项新消息:afl-tmin 现在支持 forkserver 了!
https://github.com/nccgroup/TriforceAFL Jesse Hertz [email protected] Tim Newsham [email protected]
这是 AFL 的一个补丁版本,支持使用 QEMU 进行全系统模糊测试。附带的 QEMU 已更新,可在 x86_64 系统模拟器运行时跟踪分支。新增了额外指令,用于启动 AFL 的 forkserver、进行模糊测试设置,以及标记测试用例的开始和结束。
注意:并非所有 AFL 工具都经过了新变更的测试。以下工具经过了部分测试:
构建:
make
获取覆盖图:
echo hello > /tmp/hello
./afl-showmap -o coverage.txt -QQ --
./afl-qemu-system-trace -kernel ../bzImage
-initrd ../initramfs.cpio.gz -m 1G -nographic
-append "console=ttyS0" -aflFile /tmp/hello
cat coverage.txt
进行模糊测试:
egrep ' (panic|log_store)$' ../mykern/kallsyms ffffffff8108e570 t log_store ffffffff8181064b T panic
mkdir inputs
echo hello > inputs/hello
./afl-fuzz -i inputs -o outputs -QQ --
afl-qemu-system-trace -kernel bzImage -initrd root.cpio.gz
-m 1G -nographic -append "console=ttyS0"
-aflPanicAddr ffffffff8181064b -aflDmesgAddr ffffffff8108e570
-aflFile @@
(注意:与使用 "-Q" 选项时不同,使用 "-QQ" 选项时必须指定 afl-qemu-system-trace 的完整命令行)。
有关如何使用此修改版 AFL 的更多详细信息, 请参阅我们的 Linux 系统调用模糊测试器: https://github.com/nccgroup/TriforceLinuxSyscallFuzzer。
新的 AFL 标志: -QQ - 使用 qemu 进行全系统模拟,而非用户模式(-Q)
新的 QEMU 标志: -aflFile - 包含模糊测试器输入的文件名 -aflPanicAddr - 用于 panic 检测的内核 panic 地址 -aflDmesgAddr - dmesg 日志函数的 Linux 内核地址,用于 检测日志记录并拦截日志消息
新的 QEMU 指令: 0f 24 - aflCall edi=1 startForkserver(esi=enableTicks) 启动 AFL 的 fork server。此后,每个测试用例将在独立的 fork 子进程中运行。 如果 enableTicks 非零,QEMU 在 fork 子进程后将重新启用 CPU 定时器, 否则不会启用。 edi=2 getWork(esi=ptr, edx=sz) 用下一个输入测试用例填充 ptr[0..sz]。返回实际填充的大小(<= sz)。 edi=3 startWork(esi=ptr) 告诉 AFL 开始跟踪。该参数指向一个包含两个四字(quadword)的缓冲区, 分别给出要跟踪代码的起始和结束地址。此范围之外的指令不会被跟踪。 edi=4 doneWork(esi=exitCode) 告诉 AFL 测试用例已完成。如果检测到 panic,AFL 将立即停止测试用例; 否则将一直运行到调用 doneWork 为止。指定的 exitCode 会返回给 AFL。 (代码可以——但目前不会——在测试用例期间检测到任何 dmesg 日志时, 对所有退出码按位或上值 64。)
新的 QEMU 块驱动程序: -drive filename=privmem: 该块驱动程序将驱动器的镜像保存在写时复制(copy-on-write)内存中, 因此更改永远不会持久化到磁盘。某个测试用例所做的更改与其他测试用例隔离。
由 Michal Zalewski [email protected] 编写和维护
版权所有 2013, 2014, 2015, 2016 Google Inc. 保留所有权利。 根据 Apache License 2.0 版本的条款和条件发布。
如需新版本和更多信息,请查看: http://lcamtuf.coredump.cx/afl/
要与其他用户交流心得或获取重大新功能通知, 请发送邮件至 [email protected]。
** 如果你没有时间阅读本文件,请参阅 QuickStartGuide.txt。**
模糊测试(Fuzzing)是识别真实软件中安全问题的最强大、最成熟的策略之一;迄今为止,在安全关键型软件中发现的大多数远程代码执行和权限提升漏洞都归功于它。
遗憾的是,模糊测试也相对浅显;盲目、随机的变异方式很难触及被测代码中的某些代码路径,这使得一些漏洞完全超出了该技术的覆盖范围。
人们曾多次尝试解决这个问题。早期方法之一是由 Tavis Ormandy 率先提出的语料库精炼(corpus distillation)。该方法依靠覆盖率信号,从大量高质量的候选文件语料库中选出一组有意义的种子,然后用传统方式进行模糊测试。这种方法效果非常好,但需要现成可用的语料库。此外,块覆盖率测量只能提供对程序状态非常简单的理解,长期来看对引导模糊测试工作的帮助有限。
其他更复杂的研究则侧重于程序流分析(“concolic execution”)、符号执行或静态分析等技术。所有这些方法在实验环境中都极具前景,但在实际应用中往往存在可靠性和性能问题——目前还无法成为“盲目”模糊测试技术的可行替代方案。
American Fuzzy Lop 是一款暴力模糊测试器,结合了一种极其简单但非常可靠的、由插桩引导的遗传算法。它使用改进形式的边覆盖(edge coverage),能够轻松捕捉程序控制流中细微的局部变化。
简单来说,整体算法可以概括为:
对于已发现的测试用例,还会定期进行清理,剔除那些已被更新、覆盖率更高的结果所取代的条目;此外,还会执行其他几项由插桩驱动的精简优化步骤。
作为模糊测试过程的附带成果,该工具会生成一个小型、自包含的有价值测试用例语料库。这些用例对于为其他需要大量人力或资源的测试方案提供种子输入极为有用——例如,对浏览器、办公应用、图形套件或闭源工具进行压力测试。
该模糊测试器经过全面测试,开箱即用的性能远优于盲目模糊测试或仅基于覆盖率的工具。
当源代码可用时,可以通过一个配套工具注入插桩,该工具可在任何第三方代码的标准构建过程中作为 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。
在测试库时,你需要找到或编写一个简单的程序,从 stdin 或文件中读取数据并将其传递给被测库。在这种情况下,必须将该可执行文件与插桩库的静态版本链接,或者确保在运行时加载正确的 .so 文件(通常通过设置 LD_LIBRARY_PATH 实现)。最简单的选择是静态构建,通常可以通过以下方式实现:
$ CC=/path/to/afl/afl-gcc ./configure --disable-shared
在调用 'make' 时设置 AFL_HARDEN=1,将使 CC 包装器自动启用代码加固选项,从而更容易检测简单的内存错误。
附:建议 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 或 honggfuzz 那样立即获得快速而粗略的结果,请在命令行中添加 -d 选项。
有关如何解读显示的状态信息并监控进程健康状况,请参阅 status_screen.txt 文件。如果任何 UI 元素以红色突出显示,请务必查阅该文件。
模糊测试过程将持续到你按下 Ctrl-C。至少,你应该让模糊测试器完成一个队列周期,这可能需要几个小时到一周左右的时间。
输出目录中会创建三个子目录并实时更新:
queue/ - 每个独特执行路径对应的测试用例,以及用户提供的所有起始文件。这就是第 2 节中提到的合成语料库。
在将此语料库用于任何其他目的之前,你可以使用 afl-cmin 工具将其缩小。该工具会找出一个提供等效边覆盖的更小文件子集。
crashes/ - 导致被测程序收到致命信号(例如 SIGSEGV、SIGILL、SIGABRT)的独特测试用例。条目按收到的信号分组。
hangs/ - 导致被测程序超时的独特测试用例。请注意,当默认(激进)超时设置生效时,由于延迟峰值和其他自然现象,这里可能会有些噪音。
如果相关的执行路径涉及先前记录的故障中未出现过的任何状态转换,则该崩溃和挂起被视为“独特”。如果同一个漏洞可以通过多种方式触发,过程早期会出现一些计数膨胀,但这应该会迅速减少。
崩溃和挂起的文件名与父级(非故障)队列条目相关联,这应有助于调试。
当你无法重现 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 实例大约占用一个 CPU 核心。这意味着在多核系统上,必须进行并行化才能充分利用硬件。有关如何在多个核心或多台联网机器上模糊测试同一目标的技巧,请参阅 parallel_fuzzing.txt。
默认情况下,afl-fuzz 的变异引擎针对紧凑型数据格式进行了优化——例如图像、多媒体、压缩数据、正则表达式语法或 shell 脚本。它不太适合具有特别冗长冗余措辞的语言——尤其是 HTML、SQL 或 JavaScript。
为了避免构建语法感知工具的麻烦,afl-fuzz 提供了一种方式,用可选的语言关键字、魔数头或与目标数据类型相关的其他特殊标记字典来为模糊测试过程注入种子——并在运行时利用它重建底层语法:
http://lcamtuf.blogspot.com/2015/01/afl-fuzz-making-up-grammar-with.html
要使用此功能,你首先需要按照 testcases/README.testcases 中讨论的两种格式之一创建字典;然后在命令行中通过 -x 选项将其指向模糊测试器。
无法提供底层语法更结构化的描述,但模糊测试器很可能仅凭插桩反馈就能推断出部分语法。这在实践中确实有效,例如:
http://lcamtuf.blogspot.com/2015/04/finding-bugs-in-sqlite-easy-way.html
附:即使没有提供显式字典,afl-fuzz 也会在确定性字节翻转期间通过非常密切地观察插桩,尝试提取输入语料库中现有的语法标记。这适用于某些类型的解析器和语法,但远不如 -x 模式效果好。