Fuzzing101全实践 -- (一)

安全入门
2022-08-19 11:58
142966

写在前面

本系列将详细记录 Fuzzing101 的所有项目实践,配合 AFL 源码分析系列使用,可以加深对 AFL 这款划时代 fuzz 工具的理解。希望能帮助新人可以在最快速度内掌握这款工具。Fuzzing101 的实践我们不会使用原生的 AFL,而是使用进阶的 AFL++,它比原生 AFL 多了一些实验性的功能,做了很多性能和效率上的改进,但是整体思路基本跟 AFL 一致。

通过使用 AFL++ 进行练习,再加上阅读 AFL 的源码,基本就可以完全掌握 AFL 的使用,如果对其设计理解深入,可以直接上手进行二开定制。

第一部分(1- 3)

1. Fuzzing101 - 1 Xpdf

1. 目标环境配置

这里选用的是比较旧版本的一个 xpdf:

wget https://dl.xpdfreader.com/old/xpdf-3.02.tar.gz
tar -xvzf xpdf-3.02.tar.gz

cd xpdf-3.02
./configure --prefix="$HOME/Desktop/Fuzz/training/fuzzing_xpdf/install/"
make
make install

(如果中间提示缺少 gcc 和 build-essential ,可直接使用 apt 进行安装。)

这里先使用原生 gcc 进行编译,主要是看下软件是否可以正常使用。

下载测试用例:

cd $HOME/Desktop/Fuzz/training/fuzzing_xpdf
mkdir pdf_examples && cd pdf_examples

# 下面的测试用例任选一个即可
wget https://github.com/mozilla/pdf.js-sample-files/raw/master/helloworld.pdf
wget http://www.africau.edu/images/default/sample.pdf
wget https://www.melbpc.org.au/wp-content/uploads/2017/10/small-example-pdf-file.pdf

v4ler1an fuzzing_xpdf ➜ ./install/bin/pdfinfo -box -meta ./pdf_examples/sample.pdf              
Creator:        Rave (http://www.nevrona.com/rave)
Producer:       Nevrona Designs
CreationDate:   Wed Mar  1 07:28:26 2006
Tagged:         no
Pages:          2
Encrypted:      no
Page size:      612 x 792 pts (letter)
MediaBox:           0.00     0.00   612.00   792.00
CropBox:            0.00     0.00   612.00   792.00
BleedBox:           0.00     0.00   612.00   792.00
TrimBox:            0.00     0.00   612.00   792.00
ArtBox:             0.00     0.00   612.00   792.00
File size:      3028 bytes
Optimized:      no
PDF version:    1.3

测试成功软件安装成功即可。

2. AFL++ 编译 target

软件正常编译成功之后,就可以删除了,然后使用 AFL++ 的编译器进行编译,主要是去执行插桩:

# 首先确保 AFL 的编译器可以正常执行
v4ler1an fuzzing_xpdf ➜ afl-clang-fast --version
afl-cc ++3.14c by Michal Zalewski, Laszlo Szekeres, Marc Heuse - mode: LLVM-PCGUARD
Ubuntu clang version 12.0.1-19ubuntu3
Target: x86_64-pc-linux-gnu
Thread model: posix
InstalledDir: /usr/lib/llvm-12/bin

# 使用 AFL 的编译器重新编译Xpdf

# 这里的LLVM的版本配置成自己安装的llvm的版本,建议使用11或12
# 如果在编译 AFL++ 时指定了 LLVM_CONFIG 选项,这里就可以不再单独进行变量设置
export LLVM_CONFIG="llvm-config-12"		
CC=$HOME/Desktop/Fuzz/AFLplusplus/afl-clang-fast CXX=$HOME/Desktop/Fuzz/AFLplusplus/afl-clang-fast++ ./configure --prefix="$HOME/Desktop/Fuzz/training/fuzzing_xpdf/install/"
make clean														# 先把使用 gcc 编译的 make 进行清理
make
make install

在 make 过程中可以看到 afl-clang-fast 编译器的相关输出信息:

而且已经指示出插桩了多少位置,如果用 IDA 进行反编译查看,可以看到可执行文件中的插桩代码,这一部分我们在 《AFL源码分析》系列文章中已经进行了概述。

编译完成后,再使用测试用例进行一下软件的运行情况即可。

3. 执行 fuzz

1. 准备测试用例

获取测试用例的方式有两种,第一种是自己在熟悉测试目标的情况下,自己有针对性地写用例;第二种就是直接使用网上公开的用例。针对文件格式的测试用例网上有很多,选择适合的即可。这里我们使用 fuzzingdata-master 中的 pdf 的测试用例来进行演示。

2. 开始 fuzz

我们先从最简单的 fuzz 开始:

afl-fuzz -i $HOME/Desktop/Fuzz/fuzzdata-master/fuzzing-corpus-master/pdf/mop -o $HOME/fuzzing_xpdf/out/ -s 123 -- $HOME/fuzzing_xpdf/install/bin/pdftotext @@ $HOME/fuzzing_xpdf/output

我们首先解释一下几个参数的含义:

  • -i,指定输入测试用例的路径
  • -o,指定输出的存储路径
  • -s,这是进行 fuzz 时随机数使用的种子,这里我们给的是简单值123
  • --,指目标程序
  • @@,指目标程序从输入读入,作为参数传递给目标程序;如果不加 @@ 的话,目标程序会从标准输入输出流读取输入

命令执行完成之后就会看到 fuzz 页面:

4. crash分析

在上面的 fuzz 过程中,我们在4min内发现了60个crash,保留了其中的15个(这是因为有很多 crash 的点是一样的,但是测试样本不一样,AFL++ 只保留了其中不同路径的 crash 样本):

我们需要对 crash 进行分析,这里可以是静态分析,也可以是动态分析。静态分析的话就是常规的关键点分析,动态分析可以编译出一个带符号的程序,然后使用 gdb 去动态调试,分析崩溃现场和函数执行流。我们先看下动态调试,静态分析会在后面的例子中结合 memory sanitizer 这个工具去使用。

1. 带符号编译目标程序
rm -r $HOME/fuzzing_xpdf/install
cd $HOME/fuzzing_xpdf/xpdf-3.02/
make clean
CFLAGS="-g -O0" CXXFLAGS="-g -O0" ./configure --prefix="$HOME/fuzzing_xpdf/install/"
make
make install
2. gdb 动态调试

使用gdb动态调试目标程序,输入使用 crashes 目录下的文件:

gdb --args $HOME/Desktop/Fuzz/training/fuzzing_xpdf/install/bin/pdftotext $HOME/Desktop/Fuzz/training/fuzzing_xpdf/out/default/crashes/id:000000,sig:11,src:000182,time:11158,execs:4841,op:havoc,rep:2 $HOME/Desktop/Fuzz/training/fuzzing_xpdf/output

加载后,直接 r 运行:

断下后,直接 bt 查看栈回溯:

从动态调试的结果可以清楚看到,这里是一个 getObject 的空指针引用问题。(漏洞分析本身不是我们关注的重点,感兴趣的小伙伴可以再深入调试寻找 root case。)


2. Fuzzing101 - 2 libexif

1. 目标环境配置

这次的目标是 libexif,版本为 0.6.15,这是一个库,所以我们想 fuzz 这种库的话,需要使用一个调用了该库文件的应用程序 - exif。

# 下载目标源码
wget https://github.com/libexif/libexif/archive/refs/tags/libexif-0_6_15-release.tar.gz
tar -xzvf libexif-0_6_15-release.tar.gz

# 编译并安装libexif
cd libexif-libexif-0_6_15-release/
# 编译使用了 autoconf ,所以需要安装一些额外的库,如果已经安装无需关注
sudo apt install autopoint libtool gettext libpopt-dev
autoreconf -fvi
./configure --enable-shared=no --prefix="$HOME/Desktop/Fuzz/training/fuzzing_libexif/install/"
make -j$(nproc)
make install

# 选择一个接口应用
wget https://github.com/libexif/exif/archive/refs/tags/exif-0_6_15-release.tar.gz
tar -xzvf exif-0_6_15-release.tar.gz
# 编译并安装 exif 命令行
cd exif-exif-0_6_15-release/
autoreconf -fvi
./configure --enable-shared=no --prefix="$HOME/Desktop/Fuzz/traning/fuzzing_libexif/install/" PKG_CONFIG_PATH=$HOME/Desktop/Fuzz/traning/fuzzing_libexif/install/lib/pkgconfig
make -j$(nproc)
make install

运行 exif,确保程序可以正常运行即可。

v4ler1an fuzzing_libexif ➜ ./install/bin/exif --version  
0.6.15
v4ler1an fuzzing_libexif ➜ ./install/bin/exif --help   
Usage: exif [OPTION...] file
  -v, --version                   Display software version
  -i, --ids                       Show IDs instead of tag names
  -t, --tag=tag                   Select tag
      --ifd=IFD                   Select IFD
  -l, --list-tags                 List all EXIF tags
  -|, --show-mnote                Show contents of tag MakerNote
      --remove                    Remove tag or ifd
  -s, --show-description          Show description of tag
  -e, --extract-thumbnail         Extract thumbnail
  -r, --remove-thumbnail          Remove thumbnail
  -n, --insert-thumbnail=FILE     Insert FILE as thumbnail
  -o, --output=FILE               Write data to FILE
      --set-value=STRING          Value
  -m, --machine-readable          Output in a machine-readable (tab delimited) format
  -x, --xml-output                Output in a XML format
  -d, --debug                     Show debugging messages

Help options:
  -?, --help                      Show this help message
      --usage                     Display brief usage message

2. AFL++ 编译 target

这次我们不再使用 afl-clang-fast 这种 LLVM-MODE 进行插桩编译,而是采用 AFL++ 独有的一个新的插桩模式:afl-clang-lto,这种模式会比 AFL 传统的 afl-gcc, afl-clang-fast 都要快很多(因为是在链接时进行插桩)。(对于这种模式细节感兴趣的小伙伴,可以去查看 AFL++的官方介绍 https://github.com/AFLplusplus/AFLplusplus/blob/stable/instrumentation/README.lto.md)

# 使用 afl-clang-lto 重新编译 libexif
rm -r $HOME/Desktop/Fuzz/training/fuzzing_libexif/install
cd $HOME/Desktop/Fuzz/training/fuzzing_libexif/libexif-libexif-0_6_15-release/
make clean
export LLVM_CONFIG="llvm-config-12" # llvm-config-version at least is 11
CC=afl-clang-lto ./configure --enable-shared=no --prefix="$HOME/Desktop/Fuzz/training/fuzzing_libexif/install/"
make -j$(nproc)
make install

# 使用 afl-clang-lto 重新编译 exif
cd $HOME/fuzzing_libexif/exif-exif-0_6_15-release
make clean
export LLVM_CONFIG="llvm-config-12"
CC=afl-clang-lto ./configure --enable-shared=no --prefix="$HOME/fuzzing_libexif/install/" PKG_CONFIG_PATH=$HOME/fuzzing_libexif/install/lib/pkgconfig
make -j$(nproc)
make install

3. 执行 fuzz

测试用例:

wget https://github.com/ianare/exif-samples/archive/refs/heads/master.zip
unzip master.zip

这次执行 fuzz ,我们再使用一下 AFL 的 “Master-Slave” 并行 fuzz ,类似于 Nginx 这种进程,有一个主进程,还可以有多个子进程来进行并行 fuzz。(对于并行fuzz 的底层实现和优缺点我们会在源码分析的文章中进行介绍。)

afl-fuzz -i $HOME/Desktop/Fuzz/training/fuzzing_libexif/exif-samples-master/jpg/ -o $HOME/Desktop/Fuzz/training/fuzzing_libexif/out/ -s 123 -M master -- $HOME/Desktop/Fuzz/training/fuzzing_libexif/install/bin/exif @@
afl-fuzz -i $HOME/Desktop/Fuzz/training/fuzzing_libexif/exif-samples-master/jpg/ -o $HOME/Desktop/Fuzz/training/fuzzing_libexif/out/ -s 123 -S slave1 -- $HOME/Desktop/Fuzz/training/fuzzing_libexif/install/bin/exif @@
afl-fuzz -i $HOME/Desktop/Fuzz/training/fuzzing_libexif/exif-samples-master/jpg/ -o $HOME/Desktop/Fuzz/training/fuzzing_libexif/out/ -s 123 -S slave2 -- $HOME/Desktop/Fuzz/training/fuzzing_libexif/install/bin/exif @@
afl-fuzz -i $HOME/Desktop/Fuzz/training/fuzzing_libexif/exif-samples-master/jpg/ -o $HOME/Desktop/Fuzz/training/fuzzing_libexif/out/ -s 123 -S slave3 -- $HOME/Desktop/Fuzz/training/fuzzing_libexif/install/bin/exif @@

这几个并行进程之间时信息共享的,如果有机会,我们会深入分析其实现和可以改进的地方:)。(这个速度是比较慢的,因为我直接使用的原始配置,没有进行任何的优化定制。)

4. crash 分析

因为我们是并行 fuzz 的,所以最终的 out 文件夹的目录不是单目录:

每个进程的最终的目录里都有完整的各个输出:

同理,我们仍然带符号重新进行编译:

rm install
cd libexif-libexif-0_6_15-release
make clean
CFLAGS="-g -O0" CXXFLAGS="-g -O0" ./configure --enable-shared=no --prefix="$HOME/Desktop/Fuzz/training/fuzzing_libexif/install/"
make -j$(nproc)
make install

cd exif-exif-0_6_15-release
make clean
CFLAGS="-g -O0" CXXFLAGS="-g -O0" ./configure --enable-shared=no --prefix="$HOME/Desktop/Fuzz/training/fuzzing_libexif/install/" PKG_CONFIG_PATH=$HOME/Desktop/Fuzz/training/fuzzing_libexif/install/lib/pkgconfig
make -j$(nproc)
make install

然后使用 gdb 进行动态调试,参数为 crashes 目录下的文件:

gdb --args $HOME/Desktop/Fuzz/training/fuzzing_libexif/install/bin/exif $HOME/Desktop/Fuzz/training/fuzzing_libexif/out/slave1/crashes/id:000000,sig:11,src:000349,time:315098,execs:85277,op:havoc,rep:16

漏洞深入分析部分我们暂时忽略。

5. 总结

这个例子里我们新增了 afl-clang-lto 的编译模式,新增了并行 fuzz 的使用,大家在后续的 fuzz 过程中,可以直接使用这两个新特性来提升 fuzz 的速度。

3. Fuzzing101 - 3 tcpdump

1. 目标环境配置

cd $HOME/Desktop/Fuzz/training/fuzzing_tcpdump

# download and uncompress tcpdump-4.9.2.tar.gz
wget https://github.com/the-tcpdump-group/tcpdump/archive/refs/tags/tcpdump-4.9.2.tar.gz
tar -xzvf tcpdump-4.9.2.tar.gz

# tcpdump will use libpcap, so compile the library first.
# download and uncompress libpcap-1.8.1.tar.gz
wget https://github.com/the-tcpdump-group/libpcap/archive/refs/tags/libpcap-1.8.1.tar.gz
tar -xzvf libpcap-1.8.1.tar.gz

# build and install
mv libpcap-libpcap-1.8.1/ libpcap-1.8.1/
cd libpcap--1.8.1/
./configure --enable-shared=no --prefix="$HOME/Desktop/Fuzz/training/fuzzing_tcpdump/install/"
make -j$(nproc)
make install

# build and install tcpdump
cd ../tcpdump-tcpdump-4.9.2/
CPPFLAGS=-I$HOME/Desktop/Fuzz/training/fuzzing_tcpdump/install/include/ LDFLAGS=-L$HOME/Desktop/Fuzz/training/fuzzing_tcpdump/install/lib/ ./configure --prefix="$HOME/Desktop/Fuzz/training/fuzzing_tcpdump/install/"
make -j$(nproc)
make install

编译完成后程序可以正常执行即可。

2. AFL++ 编译 target

在这个例子中,我们会引入一个新的工具 - ASan。它是 AddressSanitizer 的一个 C 和 C++的内存错误检查工具,包含一个编译器插桩模块和一个运行时库,可以发现对堆、栈和全局对象的越界访问、释放后重利用、双重释放和内存泄漏等错误。

首先清除原来的编译和安装:

rm -r $HOME/fuzzing_tcpdump/install
cd $HOME/fuzzing_tcpdump/libpcap-1.8.1/
make clean

cd $HOME/fuzzing_tcpdump/tcpdump-tcpdump-4.9.2/
make clean

然后指定 AFL_USE_ASAN=1 编译选项再使用 afl-clang-lto 进行再次编译:

cd $HOME/Desktop/Fuzz/training/fuzzing_tcpdump/libpcap-1.8.1/
export LLVM_CONFIG="llvm-config-12"
CC=afl-clang-lto ./configure --enable-shared=no --prefix="$HOME/Desktop/Fuzz/training/fuzzing_tcpdump/install/"
AFL_USE_ASAN=1 make -j$(nproc)
AFL_USE_ASAN=1 make install

cd $HOME/Desktop/Fuzz/training/fuzzing_tcpdump/tcpdump-tcpdump-4.9.2/
AFL_USE_ASAN=1 CC=afl-clang-lto ./configure --prefix="$HOME/Desktop/Fuzz/training/fuzzing_tcpdump/install/"
AFL_USE_ASAN=1 make -j$(nproc)
AFL_USE_ASAN=1 make install

3. 执行 fuzz

继续使用并行 fuzz 来加快速度。测试用例我们直接使用 tcpdump-tcpdump-4.9.2/tests

这里还需要注意的是,ASan 会使用大量的内存,所以这里需要取消内存限制 -m none:

afl-fuzz -m none -i $HOME/Desktop/Fuzz/training/fuzzing_tcpdump/tcpdump-tcpdump-4.9.2/tests/ -o $HOME/Desktop/Fuzz/training/fuzzing_tcpdump/out/ -s 123 -M master -- $HOME/Desktop/Fuzz/training/fuzzing_tcpdump/install/sbin/tcpdump -vvvvXX -ee -nn -r @@
afl-fuzz -m none -i $HOME/Desktop/Fuzz/training/fuzzing_tcpdump/tcpdump-tcpdump-4.9.2/tests/ -o $HOME/Desktop/Fuzz/training/fuzzing_tcpdump/out/ -s 123 -S slave1 -- $HOME/Desktop/Fuzz/training/fuzzing_tcpdump/install/sbin/tcpdump -vvvvXX -ee -nn -r @@
afl-fuzz -m none -i $HOME/Desktop/Fuzz/training/fuzzing_tcpdump/tcpdump-tcpdump-4.9.2/tests/ -o $HOME/Desktop/Fuzz/training/fuzzing_tcpdump/out/ -s 123 -S slave2 -- $HOME/Desktop/Fuzz/training/fuzzing_tcpdump/install/sbin/tcpdump -vvvvXX -ee -nn -r @@
afl-fuzz -m none -i $HOME/Desktop/Fuzz/training/fuzzing_tcpdump/tcpdump-tcpdump-4.9.2/tests/ -o $HOME/Desktop/Fuzz/training/fuzzing_tcpdump/out/ -s 123 -S slave3 -- $HOME/Desktop/Fuzz/training/fuzzing_tcpdump/install/sbin/tcpdump -vvvvXX -ee -nn -r @@

结果如下:

这个的 fuzz 效果就不如前面的好,一方面是因为软件本身的问题,一方面是因为我们没有对 corpus 进行任何的优化,导致覆盖到的程序路径也没有那么多,整体的效果并不是很好。但是从 fuzz 这项技术本身的角度来看,这种结果是还可以的,毕竟 fuzz 不到东西才是常态:)。

4. crash 分析

在使用 ASan 之前,我们会使用 gdb 来把目标程序附带 crashes 文件夹中的数据作为参数来进行动态调试,从而可以看到其函数调用栈、内存数据等信息。而有了 ASan 之后,我们不必再编译程序,直接把 crash 文件作为参数传入即可,ASan 会给出我们崩溃的相关信息:

ASan 会给出造成问题的原因、函数的栈回溯、出现问题的具体数据点,结合这些信息,可以快速帮助我们重现 crash,确认到 crash 的 root case。

5. 总结

在这个例子里,我们添加了 ASan 这个工具,它对于我们的 fuzz 其实是一个负面的作用,因为它的存在,在进行插桩时会造成内存的严重消耗。但是它的主要作用是在 crash 分析上,我们可以无需经过动态调试,就可以得到程序崩溃的相关信息和数据,可以更快地帮助我们找到造成 crash 的原因。

需要注意的是,因为 ASan 的大内存消耗,所以需要根据情况来决定要不要使用该工具。

阶段总结

到此为止,我们通过3个例子基本掌握了 AFL 的白盒模式下针对文件格式的 fuzz 的最基本的使用方法,后续的内容就是在这个基础上,进一步进行扩展,大部分内容是与提升 fuzz 效率和优化 corpus 等相关。

备注

我们没有对一些基础概念进行解释,比如 AFL 的一些参数、环境变量,包括对 ASan 这种 memory sanitizer 工具也只是简单提及。因为本质上它们并不属于 fuzz 流程的部分,而是“精进”的部分。感兴趣的小伙伴可以自行探索。

扩展

前面介绍的 AFL 针对的都是 PC 端的软件,确切地说都是 Linux 平台下的传统的端侧软件。如果我们想在 IoT 领域使用该工具,应该如何切入呢?IoT 领域的一些系统中,也会有诸多处理文件的软件,我们是否可以直接套用现有的思路去 fuzz 这些文件处理软件呢?比如 xml、json的处理。因为这些文件都是在前端进行组织,然后发送到后端去进行处理,如果我们能在这个阶段加入 fuzz 流程,就可以倒推 payload 的组织,从而实现漏洞利用。我们在后续会慢慢切入。

分享到

参与评论

0 / 200

全部评论 2

zebra的头像
学习大佬思路
2023-03-19 12:14
Hacking_Hui的头像
学习了
2023-02-01 14:20
投稿
签到
联系我们
关于我们