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 文件夹,输入 makesudo 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 updatesudo apt-get upgradesudo apt-get install automake autoconf build-essential llvmcd $HOMEgit clone https://github.com/AFLplusplus/AFLpluspluscd AFLplusplusmake allsudo make install 指令。

3.3.2 构建环境

步骤:

1、为 Fuzz 目标创建一个新目录。输入 cd \$HOMEmkdir fuzzing_xpdf && cd fuzzing_xpdf 指令。

2、下载 Xpdf 3.02 版本。输入 wget https://dl.xpdfreader.com/old/xpdf-3.02.tar.gztar -xvzf xpdf-3.02.tar.gz 指令。

3、构建 Xpdf。输入 cd xpdf-3.02./configure --prefix="$HOME/fuzzing_xpdf/install/"makemake install 指令。

4、准备一些 PDF 样例文件用于测试。输入 cd $HOME/fuzzing_xpdfmkdir pdf_examples && cd pdf_exampleswget https://github.com/mozilla/pdf.js-sample-files/raw/master/helloworld.pdfwget 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/installcd $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::getObjrun 指令。

输出结果

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-buildcd 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 updatesudo 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。

这是一个缓冲区溢出漏洞,当输入数据可以控制重新分配的大小,攻击者可能能够利用这个漏洞来引发程序崩溃,或者执行任意代码。