AFL Fuzz 初体验。
“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.”
American Fuzzy Lop
AFL(American Fuzzy Lop)是由安全研究员 Michal Zalewski(@lcamtuf)开发的一款基于覆盖引导(Coverage-guided)的模糊测试工具,它通过记录输入样本的代码覆盖率,从而调整输入样本以提高覆盖率,增加发现漏洞的概率。
- 从源码编译程序时进行插桩,以记录代码覆盖率(Code Coverage);
- 选择一些输入文件,作为初始测试集加入输入队列(queue);
- 将队列中的文件按一定的策略进行“突变”;
- 如果经过变异文件更新了覆盖范围,则将其保留添加到队列中;
- 上述过程会一直循环进行,期间触发了 crash 的文件会被记录下来。
Environment
安装 AFL 的方式和一般源码编译的程序一样。其中 AFL 的 QEMU 模式常用于黑盒测试。但是因为 AFL 使用的 QEMU 版本太旧,util/memfd.c
中定义的函数 memfd_create()
会和 glibc 中的同名函数冲突,AFL 在 qemu_mode 文件夹下提供了一个脚本 build_qemu_support.sh,用于 patch 一个新的 QEMU。安装完成后,可以看到有以下这些命令:
➜ afl-2.52b afl-
afl-analyze afl-clang++ afl-fuzz afl-gcc afl-plot afl-showmap afl-whatsup
afl-clang afl-cmin afl-g++ afl-gotcpu afl-qemu-trace afl-tmin
记录几个常用的命令:
命令 | 作用 |
---|---|
afl-analyze |
以指定输入为基础对程序进行分析识别出程序合法输入的轮廓 |
afl-clang++ /afl-clang |
|
afl-cmin |
尝试找到与测试样例全集具有相同覆盖范围的最小子集 |
afl-fuzz |
AFL 进行 Fuzzing 的主程序 |
afl-g++ /afl-gcc |
编译生成的文件可以通过设置 LD_LIBRARY_PATH 让程序加载经过 AFL 插桩的 .so 文件;或者可以直接加上 --disable-shared 进行静态编译 |
afl-gotcpu |
用于查看每个核心使用状态 |
afl-plot |
用于绘制各种状态指标的直观变化趋势 |
afl-showmap |
跟踪单个输入的执行路径,并打印程序执行的输出、捕获的元组 |
afl-tmin |
减小单个输入样例的大小 |
afl-whatsup |
用于查看每个 fuzzer 的运行状态和总体运行概况,加上 -s 选项只显示概况,其中的数据都是所有 fuzzer 的总和 |
Test
本文主要记录怎么用 Fuzzing 对 PWN 题漏洞点寻找。首先可以在当前目录下新建一个文件夹 in
,然后使用 tee 来记录下手动输入的一些测试样例:
tee in/pattern.txt | ./binary
接下来可以直接使用 afl-fuzz
对程序 Fuzzing。其中 -i
指定输入目录,-o
指定输出目录,-Q
表示使用 QEMU 模式:
afl-fuzz -i in -o out -Q -- ./binary
运行的时候可能会报错:
afl-fuzz 2.52b by <lcamtuf@google.com>
[+] You have 1 CPU core and 1 runnable tasks (utilization: 100%).
[*] Checking core_pattern...
[-] Hmm, your system is configured to send core dump notifications to an
external utility. This will cause issues: there will be an extended delay
between stumbling upon a crash and having this information relayed to the
fuzzer via the standard waitpid() API.
To avoid having crashes misinterpreted as timeouts, please log in as root
and temporarily modify /proc/sys/kernel/core_pattern, like so:
echo core >/proc/sys/kernel/core_pattern
[-] PROGRAM ABORT : Pipe at the beginning of 'core_pattern'
Location : check_crash_handling(), afl-fuzz.c:7275
在执行 afl-fuzz
前,如果系统配置为将核心转储文件(core)通知发送到外部程序,将导致将崩溃信息发送到 Fuzzer 之间的延迟增大,进而可能将崩溃被误报为超时,所以得临时修改 core_pattern 文件:
echo core | sudo tee /proc/sys/kernel/core_pattern
接下来就可以开始 Fuzz:
状态窗口中“cycles done”字段颜色的颜色可以作为何时停止测试的参考。随着周期数不断增大,其颜色也会由洋红色,逐步变为黄色、蓝色、绿色。当其变为绿色时,继续 Fuzzing 下去也很难有新的发现了,这时便可以通过 Ctrl-C 停止 afl-fuzz
。在输出的 out
文件夹下的 crashes
,可以看到测试得到使程序 crash 的几个样例:
➜ crashes ls
id:000000,sig:06,src:000000,op:havoc,rep:2 id:000001,sig:06,src:000000,op:havoc,rep:4 id:000002,sig:06,src:000000,op:havoc,rep:2 README.txt
使用 afl-tmin
可以获得最小的测试样例:
afl-tmin -i out/crashes/xxx -o min -Q -- ./binary
Plaid-CTF-2015-PlaidDB
以之前的一道题目为例。使用 afl-fuzz
对程序进行测试,跑了一段时间后可以得到几个使 crash 的结果:
➜ crashes xxd id:000000,sig:06,src:000000,op:havoc,rep:2
0000000: 5055 540a 410a 300a 4445 4c0a 410a 5055 PUT.A.0.DEL.A.PU
0000010: 540a 4e4e 4e4e 4e4e 0a30 0a47 4554 0a41 T.NNNNNN.0.GET.A
0000020: 415a 4141 4141 4141 4141 4141 4141 4141 AZAAAAAAAAAAAAAA
0000030: 4141 4141 4155 540a 410a AAAAAUT.A.
➜ crashes xxd id:000001,sig:06,src:000000,op:havoc,rep:4
0000000: 5055 540a 410a 300a 4445 4c0a 410a 5055 PUT.A.0.DEL.A.PU
0000010: 540a 4e4e 4e4e 4e4e 0a30 0a47 4554 0a58 T.NNNNNN.0.GET.X
0000020: 4141 ae41 4141 4141 4141 4141 4141 4141 AA.AAAAAAAAAAAAA
0000030: 4141 4139 4155 540a 410a AAA9AUT.A.
➜ crashes xxd id:000002,sig:06,src:000000,op:havoc,rep:2
0000000: 5055 540a 410a 300a 4445 4c0a 410a 5055 PUT.A.0.DEL.A.PU
0000010: 540a 4e4e 5554 0a4e 0a30 0a47 4554 0a41 T.NNUT.N.0.GET.A
0000020: 4141 410a 4e0a 300a 4745 540a 4141 4141 AAA.N.0.GET.AAAA
0000030: 4141 4141 4141 4141 4141 4141 4141 4141 AAAAAAAAAAAAAAAA
0000040: 4141 4141 0a AAAA.
然后用 afl-tmin
来将样例最小化:
➜ Plaid-CTF-2015-PlaidDB xxd min
0000000: 5055 540a 0a0a 5055 540a 0a0a 4745 540a PUT...PUT...GET.
0000010: 3030 3030 3030 3030 3030 3030 3030 3030 0000000000000000
0000020: 3030 3030 3030 3030 0a 00000000.
然后输入样例可以看到程序会 crash。发现程序在 free 的时候发现下一个 chunk 的 size 不合法,再经过一定的调试就可以定位到 Off-by-Null:
➜ Plaid-CTF-2015-PlaidDB cat min | ./datastore
INFO: Welcome to the PlaidDB data storage service.
INFO: Valid commands are GET, PUT, DUMP, DEL, EXIT
PROMPT: Enter command:
PROMPT: Enter row key:
PROMPT: Enter data size:
PROMPT: Enter data:
INFO: Insert successful.
PROMPT: Enter command:
PROMPT: Enter row key:
PROMPT: Enter data size:
PROMPT: Enter data:
INFO: Update successful.
PROMPT: Enter command:
PROMPT: Enter row key:
ERROR: Row not found.
*** Error in `./datastore': free(): invalid next size (fast): 0x000055f7ab2a20f0 ***
[1] 21002 done cat min |
21003 abort ./datastore
tcpdump-4.9.0
这里再对 64 位的 tcpdump-4.9.0 进行了测试。在安装 tcpdump 之前要先安装 libpcap:
wget http://www.tcpdump.org/release/libpcap-1.8.1.tar.gz
tar -zxvf libpcap-1.8.1.tar.gz && cd libpcap-1.8.1
./configure
make
sudo make install
然后使用用 afl-gcc
和 afl-g++
对 tcpdump 进行编译:
wget http://www.tcpdump.org/release/tcpdump-4.9.0.tar.gz
tar -zxvf tcpdump-4.9.0.tar.gz && cd tcpdump-4.9.0
CC=afl-gcc CXX=afl-g++ ./configure
make
sudo make install
安装完后查看以下版本:
$ tcpdump --version
tcpdump version 4.9.0
libpcap version 1.8.1
然后从 Wireshark 官网上下载一些流量包,作为测试的样例,并使用 editcap 将每个流量包分成四份:
$ for i in `ls` ; do editcap -c 4 $i Trimmed/trimm.pcap ; done
$ ls Trimmed/ | wc -l
32170
然后用 alf-cmin
把测试样例再缩小。需要从文件中获取输入的话,可以使用“@@”代替被测试程序命令行中输入文件名的位置:
$ afl-cmin -i Trimmed/ -o Pcap-corpus/ -- tcpdump -ee -vv -nnr @@
corpus minimization tool for afl-fuzz by <lcamtuf@google.com>
[*] Testing the target binary...
[+] OK, 377 tuples recorded.
[*] Obtaining traces for input files in 'Trimmed/'...
Processing file 32170/32170...
[*] Sorting trace sets (this may take a while)...
[+] Found 13843 unique tuples across 32170 files.
[*] Finding best candidates for each tuple...
Processing file 32170/32170...
[*] Sorting candidate list (be patient)...
[*] Processing candidates and writing output files...
Processing tuple 13843/13843...
[+] Narrowed down to 380 files, saved in 'Pcap-corpus/'.
最后使用 afl-fuzz
对程序测试,可以使用 screen
把程序放在后台运行:
$ screen afl-fuzz -i PCAPS/Pcap-corpus/ -o out -- tcpdump -ee -vv -nnr @@
据说能跑出 CVE(CVE-2017-13044 和 CVE-2017-12989),我用学生服务器跑了一个多礼拜跑出了五个 hangs。不得不说大型的 Fuzz 还是比较依赖机器的性能的:
References
https://www.youtube.com/watch?v=jEHgm7S58N8
https://cool-y.github.io/2019/07/09/afl-first-try/
https://mp.weixin.qq.com/s/G7l5wBB7oKjXCDGtjuxYTQ
https://mp.weixin.qq.com/s/WMfCNN095-PpM0VB_pRESg
https://0x00sec.org/t/fuzzing-projects-with-american-fuzzy-lop-afl/6498
https://countuponsecurity.com/2018/03/07/intro-to-american-fuzzy-lop-fuzzing-in-5-steps/
https://countuponsecurity.com/2018/04/24/intro-to-american-fuzzy-lop-fuzzing-with-asan-and-beyond/
https://blog.betamao.me/2019/02/04/Fuzz%E4%B9%8BAFL/
https://blog.csdn.net/hejunqing14/article/details/50338161