AFL++测试工具
参考:
https://github.com/AFLplusplus/AFLplusplus/blob/stable/docs/tutorials.md
项目及其准备概要
AFLplusplus/AFLplusplus: The fuzzer afl++ is afl with community patches, qemu 5.1 upgrade, collision-free coverage, enhanced laf-intel & redqueen, AFLfast++ power schedules, MOpt mutators, unicorn_mode, and a lot more!
https://github.com/antonio-morales/Fuzzing101
AFL++安装
本地安装
sudo apt-get update
sudo apt-get install -y build-essential python3-dev automake git flex bison libglib2.0-dev libpixman-1-dev python3-setuptools
sudo apt-get install -y lld-11 llvm-11 llvm-11-dev clang-11 || sudo apt-get install -y lld llvm llvm-dev clang
sudo apt-get install -y gcc-$(gcc --version|head -n1|sed 's/.* //'|sed 's/\..*//')-plugin-dev libstdc++-$(gcc --version|head -n1|sed 's/.* //'|sed 's/\..*//')-devcd $HOME
git clone https://github.com/AFLplusplus/AFLplusplus && cd AFLplusplus
export LLVM_CONFIG="llvm-config-11"
make distrib
sudo make install
docker安装
sudo apt install docker
docker pull aflplusplus/aflplusplus
docker run -ti -v $HOME:/home aflplusplus/aflplusplus
export $HOME="/home"
编译环境准备
有CMakeListis.txt文件
CC=/AFLplusplus/afl-clang-fast CXX=/AFLplusplus/afl-clang-fast++ cmake .
#设置环境变量 CC(C 编译器)为 AFLplusplus 提供的 afl-clang-fast 编译器
#设置环境变量 CXX(C++ 编译器)为 AFLplusplus 提供的 afl-clang-fast++ 编译器
#cmake .从当前目录加载CMakeLists.txt文件,生成Makefile等构建文件
make
仅有源代码
afl-clang-fast++ simple_crash.cpp -o target_fast #编译源代码
种子准备
返回根目录
mkdir seeds
for i in {0..4}; do dd if=/dev/urandom of=seed_$i bs=64 count=10; done
测试命令
afl-fuzz -i [ seeds directory] -o out -m none -d -- [full path to the executable]
测试结果
分析崩溃
- 一旦 AFL++ 的 UI 中显示至少 1 个崩溃,请点击
Ctrl + C
退出 AFL++。可以通过遍历目录来找到导致程序崩溃的输入out/default/crashes
。
代码阅读工具sourcetrail
首先进入项目的文件夹
mkdir build
cd build
cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON ..
- 使用 CMake 创建一个 JSON 文件,该文件将告诉 Sourcetrail 文件如何相互交互。
- 打开 Sourcetrail。在随应用打开的弹出窗口中,点击“新建项目”
- 为项目创建一个名称。
- 选择此 Sourcetrail 项目将要“驻留”的位置。这将是您正在运行的代码的顶级目录
- 单击窗口底部的“添加源组”按钮。
- 我们选择“"C/C++ from Compilation Database”然后单击“下一步”
- 在“Compilation Database”输入框中,点击带有三个点的圆圈。找到生成的json文件。点击打开
- 点击弹出窗口底部的“创建”。这将关闭当前弹出窗口,并出现另一个蓝色的弹出窗口。最后一步是索引所有文件——点击“开始”
对源代码进行模糊测试
准备命令
export AFL_USE_ASAN=1
该命令的作用是告诉 AFL++ 在编译目标程序时,自动启用 AddressSanitizer(地址 sanitizer,简称 ASAN) 工具。ASAN 是 LLVM/Clang 和 GCC 编译器提供的一种内存错误检测机制,能够帮助发现多种内存相关漏洞,如:
- 堆缓冲区溢出(heap buffer overflow)
- 栈缓冲区溢出(stack buffer overflow)
- 使用已释放的内存(use-after-free)
- 双重释放(double free)
- 内存泄漏(memory leak)等
sudo afl-system-config
- 关闭地址空间随机化(ASLR):减少因内存地址随机化导致的测试不稳定问题,使崩溃(crash)更容易复现。
- 优化内核调度和资源限制:如调整进程调度优先级、文件描述符限制等,提升 AFL++ 多进程测试的效率。
- 调整内存管理策略:减少不必要的内存页交换(swap),降低测试过程中的性能损耗。
- 优化文件系统缓存:减少 AFL++ 频繁读写测试文件时的 I/O 开销。
- 关闭可能影响测试稳定性的系统服务(如自动更新、日志轮转等),避免外部进程干扰模糊测试流程。
make CXX=afl-clang-fast++
- 告诉
make
工具使用afl-clang-fast++
而非默认的 C++ 编译器(如g++
或clang++
) afl-clang-fast++
会在编译过程中自动插入代码覆盖率跟踪逻辑(插桩).
测试命令
export AFL_USE_ASAN=1
afl-clang-fast++ simple_crash.cpp -o target_fast #编译源代码运行模糊测试(假设输入目录为 in/,输出目录为 out/)
afl-fuzz -i in/ -o out/ ./test @@
使用切片对目标进行模糊测试
创建切片
为了尽可能缩小测试代码的范围,我们可以通过创建切片。
QEMU模式
~/AFLplusplus/afl-fuzz -i seeds/ -o out -Q -- ./xmllint @@
xpdf模糊测试示例
xpdf 是一个开源的 PDF 文档处理工具集,主要用于解析、查看和提取 PDF
下载和构建xpdf
cd $HOME
mkdir fuzzing_xpdf && cd fuzzing_xpdf/sudo apt install build-essentialwget https://dl.xpdfreader.com/old/xpdf-3.02.tar.gz
tar -xvzf xpdf-3.02.tar.gzcd xpdf-3.02
sudo apt update && sudo apt install -y build-essential gcc
./configure --prefix="$HOME/fuzzing_xpdf/install/"
make
make installcd $HOME/fuzzing_xpdf #下载一些pdf示例
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$HOME/fuzzing_xpdf/install/bin/pdfinfo -box -meta $HOME/fuzzing_xpdf/pdf_examples/helloworld.pdf #查看工具是否正常运行
重新编译插桩
rm -r $HOME/fuzzing_xpdf/install
cd $HOME/fuzzing_xpdf/xpdf-3.02/
make cleanexport LLVM_CONFIG="llvm-config-11"
CC=/AFLplusplus/afl-clang-fast CXX=/AFLplusplus/afl-clang-fast++ ./configure --prefix="$HOME/fuzzing_xpdf/install"
make
make install
开始测试
afl-fuzz -i $HOME/fuzzing_xpdf/pdf_examples/ -o $HOME/fuzzing_xpdf/out/ -s 123 -- $HOME/fuzzing_xpdf/install/bin/pdftotext @@ $HOME/fuzzing_xpdf/output
-i表示我们必须放置输入案例(又名文件示例)的目录
-o表示 AFL++ 存储变异文件的目录
-s表示要使用的静态随机种子
@@是占位符目标的命令行,AFL 将用每个输入文件名替换它
崩溃重现
在目录中找到与崩溃对应的文件/root/fuzzing_xpdf/out/default/crashes
。文件名类似于id:000000,sig:11,src:001504+000002,time:924544,op:splice,rep:16
将此文件作为输入输给pdftotext,也就是我们刚刚测试的。
$HOME/fuzzing_xpdf/install/bin/pdftotext '$HOME/fuzzing_xpdf/out/default/crashes/<your_filename>' $HOME/fuzzing_xpdf/output#下面的是我电脑里的对应示例
$HOME/fuzzing_xpdf/install/bin/pdftotext $HOME/fuzzing_xpdf/out/default/crashes/id:000000,sig:11,src:001863,time:436505,execs:463102,op:havoc,rep:2 $HOME/fuzzing_xpdf/output
[AFL++ 01727c61c1a4] ~/fuzzing_xpdf/out/default/crashes # $HOME/fuzzing_xpdf/install/bin/pdftotext $HOME/fuzzing_xpdf/out/default/crashes/id:000000,sig:11,src:001863,time:436505,execs:463102,op:havoc,rep:2 $HOME/fuzzing_xpdf/output Error: PDF file is damaged - attempting to reconstruct xref table... Error (441): Inline image dictionary key must be a name object Error (445): Inline image dictionary key must be a name object Error (448): Inline image dictionary key must be a name object Error (481): Inline image dictionary key must be a name object Error (482): Illegal character ')' Error (482): Inline image dictionary key must be a name object Error (483): Illegal character '>' Error: Unterminated hex string Error: Bad image parameters Segmentation fault (core dumped)
如上发现成功复现,segmentation即段错误,接下来需要的解释gdb调试分析了。
gdb回溯崩溃
删除插桩版本 -> 重新编译安装
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
gdb运行->定位到错误地方->回溯查看
gdb --args $HOME/fuzzing_xpdf/install/bin/pdftotext $HOME/fuzzing_xpdf/out/default/crashes/<your_filename> $HOME/fuzzing_xpdf/output>> run
Program received signal SIGSEGV, Segmentation fault.
EmbedStream::getChar (this=0x560877428230) at Stream.cc:787
787 return str->getChar();
(gdb) ls
Undefined command: "ls". Try "help".
如上可以发现,段错误在787行
(gdb) bt
#0 EmbedStream::getChar (this=0x560877428230) at Stream.cc:787
#1 0x00005608455cf612 in Gfx::opBeginImage (this=0x5608774262c0, args=0x7ffcec2d6b20, numArgs=0) at Gfx.cc:3880
#2 0x00005608455bd6a7 in Gfx::execOp (this=0x5608774262c0, cmd=0x7ffcec2d6b10, args=0x7ffcec2d6b20, numArgs=0) at Gfx.cc:690
#3 0x00005608455bd076 in Gfx::go (this=0x5608774262c0, topLevel=1) at Gfx.cc:581
#4 0x00005608455bce4f in Gfx::display (this=0x5608774262c0, obj=0x7ffcec2d6e70, topLevel=1) at Gfx.cc:553
#5 0x0000560845616fa8 in Page::displaySlice (this=0x560877423c00, out=0x560877425fa0, hDPI=72, vDPI=72, rotate=0, useMediaBox=0, crop=0,sliceX=-1, sliceY=-1, sliceW=-1, sliceH=-1, printing=0, catalog=0x5608774254b0, abortCheckCbk=0x0, abortCheckCbkData=0x0) at Page.cc:317
#6 0x0000560845616b7e in Page::display (this=0x560877423c00, out=0x560877425fa0, hDPI=72, vDPI=72, rotate=0, useMediaBox=0, crop=1,printing=0, catalog=0x5608774254b0, abortCheckCbk=0x0, abortCheckCbkData=0x0) at Page.cc:264
#7 0x000056084561984b in PDFDoc::displayPage (this=0x560877422ba0, out=0x560877425fa0, page=1, hDPI=72, vDPI=72, rotate=0, useMediaBox=0,crop=1, printing=0, abortCheckCbk=0x0, abortCheckCbkData=0x0) at PDFDoc.cc:317
#8 0x00005608456198c9 in PDFDoc::displayPages (this=0x560877422ba0, out=0x560877425fa0, firstPage=1, lastPage=1, hDPI=72, vDPI=72,rotate=0, useMediaBox=0, crop=1, printing=0, abortCheckCbk=0x0, abortCheckCbkData=0x0) at PDFDoc.cc:330
#9 0x000056084563d4f9 in main (argc=3, argv=0x7ffcec2d71f8) at pdftotext.cc:237
这些就是调用栈
对libexif库文件进行测试
下载并安装库文件
cd $HOME
mkdir fuzzing_libexif && cd fuzzing_libexif/wget https://github.com/libexif/libexif/archive/refs/tags/libexif-0_6_14-release.tar.gz
tar -xzvf libexif-0_6_14-release.tar.gzcd libexif-libexif-0_6_14-release/
sudo apt-get install autopoint libtool gettext libpopt-dev
autoreconf -fvi
./configure --enable-shared=no --prefix="$HOME/fuzzing_libexif/install/"
make
make install
安装使用该库的软件
cd $HOME/fuzzing_libexif
wget https://github.com/libexif/exif/archive/refs/tags/exif-0_6_15-release.tar.gz
tar -xzvf exif-0_6_15-release.tar.gzcd exif-exif-0_6_15-release/
autoreconf -fvi
./configure --enable-shared=no --prefix="$HOME/fuzzing_libexif/install/" PKG_CONFIG_PATH=$HOME/fuzzing_libexif/install/lib/pkgconfig
make
make install$HOME/fuzzing_libexif/install/bin/exif
种子语料库
cd $HOME/fuzzing_libexif
wget https://github.com/ianare/exif-samples/archive/refs/heads/master.zip
unzip master.zip$HOME/fuzzing_libexif/exif-exif-0_6_15-release/exif/exif $HOME/fuzzing_libexif/exif-samples-master/jpg/Canon_40D_photoshop_import.jpg
exif是一个产看图片元数据的工具,所以这里我们的输入使用图片作为种子。
Afl-clang-lto 仪器
我们将使用afl-clang-lto作为编译器来构建 libexif。
rm -r $HOME/fuzzing_libexif/install
cd $HOME/fuzzing_libexif/libexif-libexif-0_6_14-release/
make clean
export LLVM_CONFIG="llvm-config-11"
CC=afl-clang-lto ./configure --enable-shared=no --prefix="$HOME/fuzzing_libexif/install/"
make
make installcd $HOME/fuzzing_libexif/exif-exif-0_6_15-release
make clean
export LLVM_CONFIG="llvm-config-11"
CC=afl-clang-lto ./configure --enable-shared=no --prefix="$HOME/fuzzing_libexif/install/" PKG_CONFIG_PATH=$HOME/fuzzing_libexif/install/lib/pkgconfig
make
make install
开始测试
afl-fuzz -i $HOME/fuzzing_libexif/exif-samples-master/jpg/ -o $HOME/fuzzing_libexif/out/ -s 123 -- $HOME/fuzzing_libexif/install/bin/exif @@
崩溃分析( Eclipse-CDT调试)
tcpdump测试示例
下载和安装
cd $HOME
mkdir fuzzing_tcpdump && cd fuzzing_tcpdump/
AFL++使用指南
afl-clang-lto和afl-clang-fast的选择
-
优先选
afl-clang-lto
的情况(推荐优先尝试)-
系统安装的 Clang 版本 ≥ 11(用
clang --version
可查看,比如输出clang version 14.0.0
就符合); -
目标项目支持 LTO(链接时优化)(大部分现代项目默认支持,少数老旧项目可能因编译选项冲突不支持)。
-
优势如下
- 插桩效率最高:LTO 模式会在链接阶段整合全局代码信息,插桩更 “聪明”—— 能减少冗余插桩点,同时精准覆盖代码分支,测试时程序运行速度更快。
- 覆盖率更优:对复杂代码(如库函数调用、多文件联动)的覆盖更全面,更容易发现深层逻辑的漏洞;
- 资源占用更低:生成的插桩程序体积更小,内存消耗更少,适合长时间 fuzzing。
-
export CC=afl-clang-lto ./configure # 你的项目配置命令 make -j4 # 编译
-
-
选
afl-clang-fast
的情况-
Clang 版本在 6.0~10 之间(低于 11,不支持 LTO 模式的核心优化);
-
目标项目用
afl-clang-lto
编译失败(比如报错lto1: fatal error: invalid option '-flto'
,说明项目不支持 LTO); -
对插桩效率要求不极致,更看重兼容性(
afl-clang-fast
兼容几乎所有支持 Clang 编译的项目)。export CC=afl-clang-fast make distclean # 清理之前的编译残留 ./configure make -j4
-