Fuzzing101-复现记录
2024-05-30 11:43:12

学长搞Fuzz毕设,顺道跟着学了点

寒假时,主要跟着Github上的Fuzzing-101的项目学习复现

Exercise1 - Xpdf

目标:fuzz Xpdf PDF查看器,在Xpdf 3.02中查找 CVE-2019-13208 崩溃

使用afl-clang-fast编译器构建Xpdf(插桩)

导入llvm环境变量,指明llvm的版本

1
export LLVM_CONFIG="llvm-config-11"

指定编译器

1
CC=$HOME/AFLplusplus/afl-clang-fast CXX=$HOME/AFLplusplus/afl-clang-fast++ ./configure --prefix="$HOME/fuzzing_xpdf/install/"

编译

1
2
make
make install

运行afl-fuzz

1
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-fuzz的输入案例的具体样本

@@:文件占位符,为-i目录中具体文件

处理报错

image-20240114175354734

export环境变量AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES=1

然后开始运行fuzz

image-20240114175755242

此处我们主要看overall results中的crashes提示

复现crash

重新编译Xpdf,以包含调试信息

1
2
3
4
5
6
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调试
1
gdb --args $HOME/fuzzing_xpdf/install/bin/pdftotext $HOME/fuzzing_xpdf/out/default/crashes/<your_filename> $HOME/fuzzing_xpdf/output

运行后查看栈帧,发现了循环调用

image-20240515211506469

调试分析

不断跟进直至最后的循环,可以发现最后是在一个在getObj函数中的makeStream处产生循环

image-20240515231008350

单步进入

image-20240515231815573

makeStream中调用了dictLookup函数,dictLookup函数封装了lookup函数

image-20240515231945134

然后lookup函数中又封装了find函数赖来寻找Key=Length

image-20240515232302197

取如下地址返回到lookup函数

image-20240515232613438

e->val.fetch(xref, obj)Object.fecth调用,但本质上是封装。调试发现find(key)不为空,然后再次调用xref.fetch,打印可以发现e中的valXRef对象

image-20240515232748740

跟进验证上述说法,当type == objRef && xref不为空时,调用xref->fetch函数

image-20240515232901939

跟进fetch,调用了getObj,完成循环调用

image-20240515233228467

分析原因

产生这种洞的原因是通过fuzz产生的二进制数据满足了某条死循环的执行路径,从而产生拒绝服务

Exercise 2 - libexif 0.16.14 Fuzz

libexif和exif是什么?

libexif是一个可移植C语言编写的库,作用是从图像文件读取和写入EXIF元信息

exif是一种图片格式

我们需要使用libexif解析exif

构建libexif

从GitHub上wget对应的demo版本后

安装工具链

1
sudo apt-get install autopoint libtool gettext libpopt-dev

然后使用autoreconf生成Makefile,

1
2
3
4
autoreconf -fvi
# -f 强制生成文件
# -v 显示详细的输出信息
# -i 自动运行aclocal和autoheader

静态编译,并指定生成路径

1
./configure --enable-shared=no --prefix="/home/fuzz/fuzzing_libexif/install"

然后编译

1
2
make
make install

构建exif并调用libexif解析

1
autoreconf -fvi

然后静态编译,并指明pc文件路径

1
./configure --enable-shared=no --prefix="/home/fuzz/fuzzing_libexif/install" PGP_CONFIG_PATH=/home/fuzz/fuzzing_libexif/install/lib/pkgconfig

然后编译

1
2
make
make install

测试

去网上找了个exif图片格式的图片仓库:https://github.com/ianare/exif-samples

image-20240119224157553

使用 QEMU 模式执行模糊测试

​ 添加软件源

1
2
sudo vi /etc/apt/sources.list
deb http://th.archive.ubuntu.com/ubuntu jammy main

​ 然后更新,安装最新版本的libc

1
2
sudo apt update
sudo apt install libc6

​ 进行模糊测试

image-20240521161226514

分析 crash

分析堆溢出

​ 分析其中一个crash

image-20240521170800506

​ 直接运行后查看栈帧,发现在malloc_printer中报了realloc(): invalid next size的崩溃,然后在exif_entry_fix处下断点

image-20240521192641398

​ 发现continue12次发生崩溃,通过IDA反汇编查看函数exif_entry_fix处的函数情况

image-20240521214608871

​ 此处调试循环,发现崩溃样例中的循环次数为0x66

image-20240521214736335

​ 同时由于exif_set_short函数中,$v_{13}$为2字节,所以$&v_{12}$数组每次都以2字节偏移,而$v_9$只有1字节数据

​ 在经过e->components次循环后(调试中为102次),写入2*102了字节

​ 现在查看分配的堆块大小,堆块大小在分配的堆内存前0x10字节,大于分配的堆块大小0x70个字节(0x71中的0x1为标志位,置为1表示前一个堆块正在使用),在realloc中的内存写中会存在溢出,造成覆写后续堆块的chunk_size 导致realloc抛出异常。

image-20240521224654480

image-20240521221425576

​ 那么在循环结束时,调用这个函数,这个函数封装了realloc函数

1
e->data = (unsigned __int8 *)exif_entry_realloc(e, e->data, e->size);

崩溃原因

​ 查看glibc中realloc函数源码,可以发现由于堆溢出覆盖了堆上的数据(覆盖后几乎都是0,比较下列两张图),chunksize_nomask (next) <= CHUNK_HDR_SZ满足条件,从而抛出异常

1
2
3
4
5
next = chunk_at_offset (oldp, oldsize);
INTERNAL_SIZE_T nextsize = chunksize (next);
if (__builtin_expect (chunksize_nomask (next) <= CHUNK_HDR_SZ, 0)
|| __builtin_expect (nextsize >= av->system_mem, 0))
malloc_printerr ("realloc(): invalid next size")

(循环之前)

c5d49bb354a4c9d484ecc93d55453c27

(经过循环之后)

de7bdc33ba55e7f1321f1f4b6b6c580d

Prev
2024-05-30 11:43:12
Next