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)的模糊测试工具,它通过记录输入样本的代码覆盖率,从而调整输入样本以提高覆盖率,增加发现漏洞的概率。

  1. 从源码编译程序时进行插桩,以记录代码覆盖率(Code Coverage);
  2. 选择一些输入文件,作为初始测试集加入输入队列(queue);
  3. 将队列中的文件按一定的策略进行“突变”;
  4. 如果经过变异文件更新了覆盖范围,则将其保留添加到队列中;
  5. 上述过程会一直循环进行,期间触发了 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-gccafl-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


pwn fuzz

本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!

Linux Sandbox - Seccomp
2015-PlaidCTF-PlaidDB