使用americanfuzzylop(AFL)对项目进行fuzzing

admin 2026-05-07 05:24:23 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 本文详细介绍如何使用AmericanFuzzyLop(AFL)进行fuzzing测试,包括AFL的安装配置、源码插桩编译、主从模式运行策略以及结合exploitable和gdb的自动化崩溃分析。文档通过实际案例演示了如何在12分钟内发现独特崩溃,并提供了完整的命令示例和工具链集成方案。 综合评分: 78 文章分类: 安全工具,漏洞分析,渗透测试,安全开发


cover_image

使用 american fuzzy lop (AFL) 对项目进行 fuzzing

0x434b 0x434b

securitainment

2026年5月6日 10:24 中国香港

在小说阅读器读本章

去阅读

| 原文链接 | 作者 | | — | — | | https://0x434b.dev/fuzzing-projects-with-american-fuzzy-lop-afl/ | 0x434b |

前言

本文将以一篇短小的介绍,带你了解 fuzzer 是什么、它们如何工作,以及如何正确地配置 afl – american fuzzy lop 这款 fuzzer,从而在任意项目中发现缺陷。

afl 的知名替代工具 (用于相同或其他场景):

  • boofuzz: Network Protocol Fuzzing for Humans
  • Googles – OSS-Fuzz – Continuous Fuzzing for Open Source Software
  • libfuzzer
  • 以及更多

什么是 fuzzing?

简而言之,我们可以将 fuzzing 定义如下

"Fuzzing is a Black Box software testing technique, which basically consists in finding implementation bugs using malformed/semi-malformed data injection in an automated fashion."

这种方法可以应用于整个应用程序、特定协议,甚至单个文件格式。根据攻击向量的不同,产出的结果显然会有所变化,并可能导致数量不等的 bug。

Fuzzing 的优点

  • 设计简单,因此基础的 fuzzer 可以轻松地从零开始实现
  • 通过随机化的方式发现潜在的 bug 或缺陷,而这些往往会被人工 QA 所忽视
  • 可以将不同的输入变异方式与符号执行 (symbolic execution) 结合起来!

不那么妙的地方…

  • 往往只能找到”简单的 bug”
  • 黑盒测试方式使得评估所发现结果的影响变得困难
  • 许多 fuzzer 仅限于某种特定的协议 / 架构 /…

如何为 fuzzing 配置 afl,并搭配 exploitable 与 gdb 一起使用

我们直接进入环境配置的环节… 在此之前没什么好说的。

精彩内容马上呈现!

通过克隆仓库让 afl 跑起来

git clone https://github.com/mirrorer/afl.git afl
cd afl
make && sudo make install
su root
echo core >/proc/sys/kernel/core_pattern
cd /sys/devices/system/cpu &&echo performance | tee cpu*/cpufreq/scaling_governor
exit
sudo apt install gnuplot
# --------------------------------------------------------------------------- #
git clone https://github.com/rc0r/afl-utils.git afl-utils
cd afl-utils
sudo python setup.py install
# --------------------------------------------------------------------------- #
# -----------------------------------optional-------------------------------- #
# --------------------------------------------------------------------------- #
# check the official git repo for needed/supported architectures #
git clone https://github.com/shellphish/afl-other-arch.git afl-qemu-patch
cd afl-qemu-patch
./build.sh&nbsp;<list,of,arches,you,need>

安装完成之后,你就可以对自己心仪的项目开始 fuzzing 了。我们会在下一段中通过挑选一个随机的 github 项目来演示这一点。我会在文章末尾给出后文展示结果时所使用的 afl 命令,但出于隐私考虑不会公开被 fuzz 的仓库名。


为 afl 插桩,开始通过 pwn 协助加固 GitHub 仓库的安全

如果源代码可用,使用 CC=afl-gcc make进行编译,或使用 CC=afl-gcc cmake CMakeLists.txt && make来为 afl 插桩。

$ cd targeted_application
CC=afl-gcc cmake&nbsp;CMakeLists.txt&nbsp;&&&nbsp;make
--TheC&nbsp;compiler identification&nbsp;isGNU5.4.0
--Checkfor&nbsp;working&nbsp;C&nbsp;compiler:/usr/local/bin/afl-gcc
--Checkfor&nbsp;working&nbsp;C&nbsp;compiler:/usr/local/bin/afl-gcc&nbsp;--&nbsp;works
--DetectingC&nbsp;compiler&nbsp;ABI&nbsp;info
--DetectingC&nbsp;compiler&nbsp;ABI&nbsp;info&nbsp;-&nbsp;done
--DetectingC&nbsp;compile features
--DetectingC&nbsp;compile features&nbsp;-&nbsp;done
--Configuring&nbsp;done
--Generating&nbsp;done
--Build&nbsp;files have been written to:/home/lab/Git/<target>
Scanning&nbsp;dependencies of target <target>
[&nbsp;14%]&nbsp;BuildingCobject&nbsp;<target>
afl-cc&nbsp;2.52b by <[email protected]>
afl-as2.52b by <[email protected]>
[+]&nbsp;Instrumented5755&nbsp;locations (64-bit, non-hardened mode, ratio&nbsp;100%).
[&nbsp;28%]&nbsp;LinkingC&nbsp;static library <target>
[&nbsp;28%]&nbsp;Built&nbsp;target <target>
Scanning&nbsp;dependencies of target md2html
[&nbsp;42%]&nbsp;BuildingCobject&nbsp;<target>
afl-cc&nbsp;2.52b by <[email protected]>
afl-as2.52b by <[email protected]>
[+]&nbsp;Instrumented165&nbsp;locations (64-bit, non-hardened mode, ratio&nbsp;100%).
[&nbsp;57%]&nbsp;BuildingCobject&nbsp;<target>
afl-cc&nbsp;2.52b by <[email protected]>
afl-as2.52b by <[email protected]>
[+]&nbsp;Instrumented8&nbsp;locations (64-bit, non-hardened mode, ratio&nbsp;100%).
[&nbsp;71%]&nbsp;BuildingCobject&nbsp;<target>
afl-cc&nbsp;2.52b by <[email protected]>
afl-as2.52b by <[email protected]>
[+]&nbsp;Instrumented58&nbsp;locations (64-bit, non-hardened mode, ratio&nbsp;100%).
[&nbsp;85%]&nbsp;BuildingCobject&nbsp;<target>
afl-cc&nbsp;2.52b by <[email protected]>
afl-as2.52b by <[email protected]>
[+]&nbsp;Instrumented407&nbsp;locations (64-bit, non-hardened mode, ratio&nbsp;100%).
[100%]&nbsp;LinkingC&nbsp;executable <target>
afl-cc&nbsp;2.52b by <[email protected]>
[100%]&nbsp;Built&nbsp;target <target>

要启动针对本地应用程序的 fuzzing,可以通过下面的命令链来运行 afl:

afl-fuzz -i input_sample_dir -o output_crash_dir ./binary @@
-i&nbsp; defines&nbsp;a&nbsp;folder which holds sample data for the fuzzer to use
-o defines&nbsp;a&nbsp;folder where afl will save the fuzzing results
./binary describes the targeted application

如果你的资源足够再多启动几个 afl 进程,请记住:每个进程都会占用一个 CPU 核心,并且几乎会榨取它 100% 的算力。要做到这一点,需要对 afl 命令链稍作调整!

afl-fuzz -i input_sample_dir -o output_crash_dir -M master ./binary @@
afl-fuzz -i input_sample_dir -o output_crash_dir -S slaveX ./binary @@

master 与 slave 模式之间唯一的区别在于:master 实例仍会执行确定性 (deterministic) 检查,而 slave 则会直接进入随机变异阶段。如果你完全不想做确定性 fuzzing,大可直接只启动 slave 即可。不过,对于统计与行为研究来说,保留一个 master 进程总归是件好事。

注意:对于从文件读取输入的程序,请使用 ‘@@’ 在目标命令行中标记应放置输入文件名的位置。fuzzer 会替你完成替换。

注意 2:你既可以在 input_sample_dir 中放一个空文件让 afl 自行寻找合适的输入,也可以为正在 fuzz 的程序提供一些可解析的、有上下文针对性的输入!

要为 afl-QEMU 插桩 以进行黑盒 fuzzing,需要先安装相关依赖 sudo apt-get install libtool libtool-bin automake bison libglib2.0-dev zlib1g-dev,然后在 afl 仓库的 ~/afl/qemu_mode/目录中执行 ./build_qemu_support.sh

接下来,不要使用CC=afl-gcc来编译目标程序,并将 afl-fuzz 命令链改成下面这样:

afl-fuzz -Q -i input_sample_dir -o output_crash_dir -M master ./binary @@

至此,模拟应当已经可以独立工作了。如果想让 afl 支持其他更冷门的架构,请应用前面准备工作里提到的那个补丁!

上图展示了 master 与 slave 之间的差异,以及启动 fuzzing 流程之后 afl 的整体界面。如图所示,我们的 slave 仅凭随机 fuzzing 行为,在区区 12 分钟内就发现了一批独特的崩溃 (unique crashes)。而 master 实例此刻还远没有追上…

崩溃 (crashes) 与挂起 (hangs) 可以分别在 output_crash_dir/process_name/crashes和 output_crash_dir/process_name/hangs目录中手动逐个查看。由于这种人工劳动既乏味又低效,一些聪明人为我们准备了 afl-utils 包,它可以自动完成崩溃分析,并搭配 gdb 脚本输出漂亮的结果。


对所产生崩溃的自动化分析

要在 fuzzing 进程仍在运行的同时,使用 afl-utils 包 中的 afl-collect 配合 exploitable 来自动收集并分析崩溃,可以执行下面的操作:

afl-collect -d crashes.db -e gdb_script -r -rr ./output_crash_dir_from_afl_fuzz ./afl_collect_output_dir -j 8 -- /path/to/target

这里只需要修改两个参数:./output_crash_dir_from_afl_fuzz,即 afl-fuzz 进程保存输出的目录;另一个是 /path/to/target,即被 fuzz 的应用程序。你可以根据自己的硬件状况调整 -j 8参数,它用于指定分析输出时使用的线程数。如果一切运转正常,你就会看到类似下面这样的输出:

afl-collect -d crashes.db&nbsp;-e gdb_script -r -rr ./out ./output_aflc -j&nbsp;8&nbsp;-- ./path/to/target
afl-collect&nbsp;1.33a&nbsp;by rc0r <[email protected]> #&nbsp;@_rc0r
Crash sample collection and processing utility&nbsp;for&nbsp;afl-fuzz.

[*] Going to collect crash samples from '/home/lab/Git/code/path/to/target/out'.
[!] Table 'Data' not found&nbsp;in&nbsp;existing database!
[*] Creating new table 'Data'&nbsp;in&nbsp;database '/home/lab/Git/code/path/to/target/crashes.db' to store data!
[*] Found&nbsp;3&nbsp;fuzzers, collecting crash samples.
[*] Successfully indexed&nbsp;56&nbsp;crash samples.
***Errorin&nbsp;`/home/lab/Git/code/path/to/target':&nbsp;double free or&nbsp;corruption&nbsp;(out):0x000000000146c5a0&nbsp;***
=======&nbsp;Backtrace:&nbsp;=========
/lib/x86_64-linux-gnu/libc.so.6(+0x777e5)[0x7f0acaeb67e5]
/lib/x86_64-linux-gnu/libc.so.6(+0x8037a)[0x7f0acaebf37a]
/lib/x86_64-linux-gnu/libc.so.6(cfree+0x4c)[0x7f0acaec353c]
/home/lab/Git/code/path/to/target(<func_a>+0x93fd)[0x4627ed]
/home/lab/Git/code/path/to/target(<func_b>+0xaa)[0x40e75a]
/home/lab/Git/code/path/to/target(main+0x4c4)[0x4017f4]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf0)[0x7f0acae5f830]
/home/lab/Git/code/path/to/target(_start+0x29)[0x402169]
=======&nbsp;Memory map:&nbsp;========
00400000-00401000&nbsp;r--p&nbsp;00000000&nbsp;fd:0038669039&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;/home/lab/Git/code/path/to/target/
00401000-00476000&nbsp;r-xp&nbsp;00001000&nbsp;fd:0038669039&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;/home/lab/Git/code/path/to/target/l
00476000-0048a000&nbsp;r--p&nbsp;00076000&nbsp;fd:0038669039&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;/home/lab/Git/code/path/to/target/
0048a000-0048b000&nbsp;r--p&nbsp;00089000&nbsp;fd:0038669039&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;/home/lab/Git/code/path/to/target
0048b000-0048c000&nbsp;rw-p&nbsp;0008a000&nbsp;fd:0038669039&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;/home/lab/Git/code/path/to/target
01461000-0148a000&nbsp;rw-p&nbsp;0000000000:000&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; [heap]
7f0ac4000000-7f0ac4021000&nbsp;rw-p&nbsp;0000000000:000
7f0ac4021000-7f0ac8000000&nbsp;---p&nbsp;0000000000:000
7f0acac29000-7f0acac3f000&nbsp;r-xp&nbsp;00000000&nbsp;fd:0040899039&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;/lib/x86_64-linux-gnu/libgcc_s.so.1
7f0acac3f000-7f0acae3e000&nbsp;---p&nbsp;00016000&nbsp;fd:0040899039&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;/lib/x86_64-linux-gnu/libgcc_s.so.1
7f0acae3e000-7f0acae3f000&nbsp;rw-p&nbsp;00015000&nbsp;fd:0040899039&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;/lib/x86_64-linux-gnu/libgcc_s.so.1
7f0acae3f000-7f0acafff000&nbsp;r-xp&nbsp;00000000&nbsp;fd:0040895232&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;/lib/x86_64-linux-gnu/libc-2.23.so
7f0acafff000-7f0acb1ff000&nbsp;---p&nbsp;001c0000&nbsp;fd:0040895232&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;/lib/x86_64-linux-gnu/libc-2.23.so
7f0acb1ff000-7f0acb203000&nbsp;r--p&nbsp;001c0000&nbsp;fd:0040895232&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;/lib/x86_64-linux-gnu/libc-2.23.so
7f0acb203000-7f0acb205000&nbsp;rw-p&nbsp;001c4000&nbsp;fd:0040895232&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;/lib/x86_64-linux-gnu/libc-2.23.so
7f0acb205000-7f0acb209000&nbsp;rw-p&nbsp;0000000000:000
7f0acb209000-7f0acb22f000&nbsp;r-xp&nbsp;00000000&nbsp;fd:0040895230&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;/lib/x86_64-linux-gnu/ld-2.23.so
7f0acb401000-7f0acb404000&nbsp;rw-p&nbsp;0000000000:000
7f0acb42d000-7f0acb42e000&nbsp;rw-p&nbsp;0000000000:000
7f0acb42e000-7f0acb42f000&nbsp;r--p&nbsp;00025000&nbsp;fd:0040895230&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;/lib/x86_64-linux-gnu/ld-2.23.so
7f0acb42f000-7f0acb430000&nbsp;rw-p&nbsp;00026000&nbsp;fd:0040895230&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;/lib/x86_64-linux-gnu/ld-2.23.so
7f0acb430000-7f0acb431000&nbsp;rw-p&nbsp;0000000000:000
7ffd1292a000-7ffd1294b000&nbsp;rw-p&nbsp;0000000000:000&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; [stack]
7ffd129c9000-7ffd129cc000&nbsp;r--p&nbsp;0000000000:000&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; [vvar]
7ffd129cc000-7ffd129ce000&nbsp;r-xp&nbsp;0000000000:000&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; [vdso]
ffffffffff600000-ffffffffff601000 r-xp&nbsp;0000000000:000&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; [vsyscall]

如你所见,我们针对每一个崩溃都得到了一份内存映射和一份回溯 (backtrace)。由于这里展示了 56 个崩溃样本,我对输出做了精简以便阅读,但希望它已经把要点展示得足够清楚了。真正的硬核部分马上就来!

我们能够完整地看到是哪个进程、哪种算法产生了该错误。此外,还能看到错误类型,以及关于其是否可被利用 (exploitable) 的估计。这就为我们打开了一扇深入挖掘 /afl_out/process_name/crash_id/的窗口,该目录里保存的正是用于产生该次崩溃的输入。我们随后可以对其进行分析,尝试得出崩溃发生的原因,甚至产出一个或多个 PoC 来利用这种行为!目前一个比较大的缺点是,exploitable 脚本只能处理最常见的架构 (x86 和 ARM)!如果你想 fuzz MIPS 和 PowerPC,就需要去 fork 官方仓库,并自行为其编写相应的逻辑!

为目标应用程序构造 PoC 也变得容易得多,因为我们可以直接进入 gdb,在被 fuzz 的程序上执行那次崩溃!只需在命令行中运行下面的命令:

$ gdb ./fuzzed_application
gdb>&nbsp;run /path/to/crash_folder/crash_id

如果我们再装上像 pwndbg 或 gdb-peda 这样的 gdb 扩展,排查问题就会变得轻而易举!

我们可以一眼看清寄存器的状态,同时也能大致了解是哪个函数因生成的输入而发生了崩溃。接下来,我们就可以深入到实际的源代码中,找出它为什么会在那里崩溃。这段输入到底是怎么把程序搞乱的?当你找到这个问题的答案时,就可以亲手构造一份畸形输入并为其编写 PoC。

为了让你直观地看到 afl 究竟把我用于触发本次崩溃的真实输入改造成了什么样,我下面会把原始输入与 afl 所产生、最终在所示状态触发目标崩溃的那份输入,做一个并排对比展示:

绿色字节表示两份文件在该位置仍然相同。红色字节表示存在差异,意味着 afl 自主地对这些字节进行了变异 (右侧的就是被 afl 变异过的那一份)。


用图表呈现 afl 的运行结果

对那些痴迷于数字和统计的人而言,afl 也为我们准备了一个绝佳的特性!对每一个被启动的进程,我们都能拿到可绘图的数据!

$ ls
crashes &nbsp;fuzz_bitmap &nbsp;fuzzer_stats &nbsp;hangs &nbsp;out &nbsp;plot_data &nbsp;queue

$ afl-plot&nbsp;--help
progress plotting utility for afl-fuzz by&nbsp;<[email protected]>

This program generates gnuplot images&nbsp;from&nbsp;afl-fuzz output data. Usage:

/usr/local/bin/afl-plot afl_state_dir graph_output_dir

$ afl-plot . out
progress plotting utility for afl-fuzz by&nbsp;<[email protected]>

[*] Generating plots...
[*] Generating&nbsp;index.html...
[+] All done&nbsp;-&nbsp;enjoy your charts!

这会生成 3 张图:

  • 一张展示每秒的执行速度,
  • 一张展示路径覆盖率,
  • 还有一张展示发现的崩溃 (crashes) 与挂起 (hangs)。

在我为本文准备的这个具体 fuzzing 示例中,它们看起来是这样的:

关于这一节的最后一点说明:在 fuzzing 进程从开始运行直到终止期间,afl 界面上展示的那些统计数据,也会针对每个进程分别保存到一个独立的文件里!


总结

Fuzzing 提供了一种强有力的方式,用来测试项目代码中的故障与缺陷。根据所使用 fuzzer 的不同,生成的输出可以直接用来推导出可能的 exploit 或 PoC。

就 american fuzzy lop 而言,其基础功能本身就已经相当出色,绝对算得上现有 fuzzing 工具中速度较快的那一类。再配合上 afl-utils 与 exploitable gdb 脚本,效果就更加令人惊艳。

最后,但同样重要的一点是:不妨进一步测试 OSS、boofuzz 或其他文中没有提到的 fuzzing 框架,看看它们之间各自表现如何、谁能更胜一筹。

希望这份简短粗略的概览能让你看到:fuzzing 是一种强有力的方法,可以通过发现人工 QA 容易忽视的关键缺陷来加固应用程序。请注意,本文展示的演示是在一个相当”千疮百孔”的仓库上完成的。如果你开始对各种东西做 fuzz 却没有冒出多少崩溃,那其实是件好事,你不应该为此沮丧 — 尤其当被 fuzz 的是你自己写的代码,或是被广泛使用的代码时 🙂 !

带着这份心态:祝你 fuzzing 愉快!


免责声明:本博客文章仅用于教育和研究目的。提供的所有技术和代码示例旨在帮助防御者理解攻击手法并提高安全态势。请勿使用此信息访问或干扰您不拥有或没有明确测试权限的系统。未经授权的使用可能违反法律和道德准则。作者对因应用所讨论概念而导致的任何误用或损害不承担任何责任。


免责声明:

本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。

任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。

本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我

本文转载自:securitainment 0x434b 0x434b《使用 american fuzzy lop (AFL) 对项目进行 fuzzing》

评论:0   参与:  0