Fuzzy-Testing-Technology
实验目的
1、学习模糊测试的基本理论和操作技巧
2、深入理解模糊测试在现代软件开发中的实际应用,特别是在提高软件安全性方面的重要作用。
实验原理
模糊测试是一种自动化的软件测试技术,通过对程序输入进行随机变异生成大量的测试数据,以此检测程序在非预期输入下的行为,尤其是安全漏洞。American Fuzzy Lop(AFL)是一种流行的模糊测试工具,采用编译时插桩技术和遗传算法优化测试用例生成过程,提高了测试的效率和覆盖率。基于 AFL 的进一步发展,AFL++ 引入了新的优化和功能扩展,能更有效地支持大型项目的模糊测试需求。
实验环境
SEED Labs 2.0(64 位版)虚拟机。
Task 1:使用十六进制编辑器修改 Bare-Metal 二进制文件
Task 1:初探 AFL
Task 1.a:安装 AFL
步骤:
1、输入 git clone https://github.com/google/AFL.git
克隆最新版本的源码,输入 cd AFL
进入 AFL 文件夹,输入 make
和 sudo make install
安装 AFL。
2、输入 ls /usr/local/bin/afl*
查看文件发现安装成功。
Task 1.b:初步尝试
步骤:
1、在 Task 1 文件夹中,输入 afl-gcc -g -o test test.c
指令编译 test.c 文件,生成 test 可执行文件。
2、输入 mikir fuzz_in fuzz_out
指令创建 fuzz_in 和 fuzz_out 文件夹,输入 echo 'aaa' > ./fuzz_in/case
指令准备一个测试用例。
3、输入 afl-fuzz -i ./fuzz_in -o ./fuzz_out ./test
命令进行模糊测试。
Q: 请使用 xxd 查看你触发 crash 的 cases,并判断分别对应 test.c 中哪种漏洞,并解释漏洞产生的原因。注意,你的 crash cases 应覆盖所有 4 个漏洞。
A:进入 fuzz_in 文件夹下的 crashes 目录,可以看到所有的7个 crash cases。
漏洞1: 如果输入的字符串的首字符为C并且长度为30,则异常退出:
输入 xxd id:000002,sig:11,src:000001+000002,op:splice,rep:8
指令查看第3个 crash case,发现这种情况下,输入的字符串的首字符为C并且长度为30。
漏洞2: 如果输入的字符串的首字符为FAS并且长度为6,则异常退出:
输入 xxd id:000001,sig:11,src:000004,op:arith8,pos:2,val:+25
指令查看第2个 crash
case,发现这种情况下,输入的字符串的前三个字符为FAT并且长度为6。
漏洞3:存在栈溢出漏洞:
输入 xxd id:000003,sig:06,src:000004,op:havoc,rep:128
指令查看第4个 crash case,发现这种情况下,输入的字符串满足栈溢出条件。
漏洞 4: 存在格式化字符串漏洞:
输入 xxd id:000004,sig:06,src:000004,op:havoc,rep:16
指令查看第5个 crash case,发现这种情况下,输入的字符串含有%格式化字符串漏洞。
Task 2: 基于 AFL++ 测试 Xpdf
3.3.1 AFL++ 安装
步骤:
安装必要的 packages。输入 sudo apt-get update
, sudo apt-get upgrade
,sudo apt-get install automake autoconf build-essential llvm
,cd $HOME
,git clone https://github.com/AFLplusplus/AFLplusplus
,cd AFLplusplus
,make all
和sudo make install
指令。
3.3.2 构建环境
步骤:
1、为 Fuzz 目标创建一个新目录。输入 cd \$HOME
和 mkdir fuzzing_xpdf && cd fuzzing_xpdf
指令。
2、下载 Xpdf 3.02 版本。输入 wget https://dl.xpdfreader.com/old/xpdf-3.02.tar.gz
和 tar -xvzf xpdf-3.02.tar.gz
指令。
3、构建 Xpdf。输入 cd xpdf-3.02
, ./configure --prefix="$HOME/fuzzing_xpdf/install/"
,make
和 make install
指令。
4、准备一些 PDF 样例文件用于测试。输入 cd $HOME/fuzzing_xpdf
,mkdir pdf_examples && cd pdf_examples
,wget https://github.com/mozilla/pdf.js-sample-files/raw/master/helloworld.pdf
和 wget https://www.melbpc.org.au/wp-content/uploads/2017/10/small-example-pdf-file.pdf
指令。
5、测试 pdfinfo 二进制文件。输入 cd $HOME/fuzzing_xpdf
和 ./install/bin/pdfinfo -box -meta ./pdf_examples/helloworld.pdf
指令。
3.3.3 开始 Fuzz
步骤:
1、清理所有先前编译的目标文件和可执行文件。输入 rm -r $HOME/fuzzing_xpdf/install
,cd $HOME/fuzzing_xpdf/xpdf-3.02/
和 make clean
指令。
2、使用 afl-clang-fast 编译器构建 xpdf。输入CC=$HOME/AFLplusplus/afl-clang-fast CXX=$HOME/AFLplusplus/afl-clang-fast++ ./configure --prefix="$HOME/fuzzing_xpdf/install/"
,make 和 make install 指令。
3、使用 AFL++ 进行模糊测试。输入 $HOME/AFLplusplus/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
指令。
发现了6个 crash cases。
4、配置 gdb 进行调试。输入 gdb --args $HOME/fuzzing_xpdf/install/bin/pdftotext $HOME/fuzzing_xpdf/out/default/crashes/id:000000,sig:11,src:000001,time:100335,execs:,op:havoc,rep:2 $HOME/fuzzing_xpdf/outputn
指令。
输入 run
指令运行。发现错误类型为 SIGSEGV。
输入 bt
指令回溯堆栈。
Q:在实验报告中,请解释此现象的产生原因。
A:从 GDB 输出可以看出,程序在执行 __GI__IO_file_xsgetn 函数时发生了段错误(SIGSEGV)。该函数从 fileops.c 文件中读取数据,但是找不到该文件。从 bt 回溯中可以发现,此错误是由于在函数 Parser::makeStream 中创建文件时发生错误。
5、在 Parser::getObj() 函数设置断点并执行程序。输入 b Parser::getObj
和 run
指令。
Q:请根据上述步骤结合 gdb 的使用,进行 crash 的复现和分析。完成后,请描述观察结果,并解释引起这种漏洞的原因。
A:crash 的复现:输入 gdb --args $HOME/fuzzing_xpdf/install/bin/pdftotext $HOME/fuzzing_xpdf/out/default/crashes/id:000000,sig:11,src:000001,time:100335,execs:87679,op:havoc,rep:2 $HOME/fuzzing_xpdf/outputn
指令即可(见图17和图18)。
crash的分析:输入 c 继续运行,发现程序回到了 Parser::getObj 函数,而堆栈在不断使用。可以发现该程序是由于某种原因一直重复 Parser::getObj 函数直至堆栈耗尽而出错。接下来输入 n 一步步调试该程序:
Parser::getObj (this=0x5555556c9c10, obj=0x7fffffffdc70,
fileKey=0x0, encAlgorithm=cryptRC4, keyLength=0x0, objNum=0x0, objGen=0x0)
at Parser.cc:41
int objNum, int objGen) {
Object obj2;
if (inlineImg == 2) {
if (buf1.isCmd("[")) {
} else if (buf1.isCmd("<<")) {
} else if (buf1.isInt()) {
num = buf1.getInt();
shift();
if (buf1.isInt() && buf2.isCmd("R")) {
obj->initInt(num);
return obj;
}
XRef::readXRefTable (this=0x5555556ca230, parser=0x5555556c9c10,
pos=0x7fffffffdd2c) at XRef.cc:397
entry.gen = obj.getInt();
obj.free();
parser->getObj(&obj);
Parser::getObj (this=0x5555556c9c10, obj=0x7fffffffdc70,
fileKey=0x0, encAlgorithm=cryptRC4, keyLength=0x0, objNum=0x0, objGen=0x0)
at Parser.cc:41
int objNum, int objGen) {
发现迭代的 Parser::getObj 函数的参数未发生变化,于是导致了无限循环。
Task 3: 使用 QEMU 模式执行模糊测试(无程序源码)
3.4.1 安装 QEMU 模式(AFLplusplus 文件夹下)
步骤:
1、安装 QEMU 模式所需的依赖包。输入 sudo apt-get install libglib2.0-dev ninja-build
,cd qemu_mode
和 ./build_qemu_support.sh
指令。
发现显示 [+] libqasan ready 和 [+] all和 sudo done for qemu_mode, enjoy!,说明安装成功。
2、返回到上级目录并重新安装 AFL++。输入 cd ..
和 sudo make install
指令。
3、验证 afl-qemu-trace 是否已正确安装在 bin 目录下。输入 ls /usr/local/bin/afl*
指令。
3.4.2 使用 QEMU 模式执行模糊测试
步骤:
1、更新 libc 库。输入 sudo vi /etc/apt/sources.list
指令打开sources.list文件以添加新的源,在文件中添加 deb http://th.archive.ubuntu.com/ubuntu jammy main
来更新 libc。
2、更新系统软件源列表并安装最新版本的 libc。输入 sudo apt update
和 sudo apt install libc6
指令。
3、输入 $HOME/AFLplusplus/afl-fuzz -Q -i $HOME/qemu_fuzz/exif-samples/jpg/ -o $HOME/qemu_fuzz/out -s 123 -- $HOME/qemu_fuzz/install/bin/exif @@
进行模糊测试。
3.4.3 分析得到的 crash
在 crashes 文件夹中,出现了如下的14个 crash cases。
Q:请研究该程序中已知的 CVE-2009-3895 或 CVE-2012-2836 漏洞。结合 afl-fuzz -Q 和 gdb 工具,按照 Task 2 的步骤,找出至少一个漏洞的原因,并详细解释如何产生这些漏洞。
A:crash 的复现:输入 gdb --args $HOME/qemu_fuzz/install/bin/exif ./id:000000,sig:06,src:000060+000464,time:2201871,execs:803513,op:splice,rep:6
指令。
crash 的分析:输入 bt
指令回溯。
这个堆栈跟踪显示了一个程序崩溃,原因是在尝试重新分配内存时出现了问题。具体来说,错误消息 "realloc(): invalid next size" 表示程序试图重新分配的内存块的大小不正确。
在堆栈跟踪中,我们可以看到这个问题发生在 exif_entry_realloc 函数中,这个函数试图重新分配一个 ExifEntry 结构的内存。这个函数是在 exif_entry_fix 函数中调用的,该函数试图修复一个 ExifEntry。
这是一个缓冲区溢出漏洞,当输入数据可以控制重新分配的大小,攻击者可能能够利用这个漏洞来引发程序崩溃,或者执行任意代码。