学长搞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 | make |
运行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
目录中具体文件
处理报错
export
环境变量AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES=1
然后开始运行fuzz
此处我们主要看overall results
中的crashes
提示
复现crash
重新编译Xpdf,以包含调试信息
1 | rm -r $HOME/fuzzing_xpdf/install |
然后使用gdb调试
1 | gdb --args $HOME/fuzzing_xpdf/install/bin/pdftotext $HOME/fuzzing_xpdf/out/default/crashes/<your_filename> $HOME/fuzzing_xpdf/output |
运行后查看栈帧,发现了循环调用
调试分析
不断跟进直至最后的循环,可以发现最后是在一个在getObj
函数中的makeStream
处产生循环
单步进入
makeStream中调用了dictLookup函数,dictLookup函数封装了lookup函数
然后lookup函数中又封装了find函数赖来寻找Key=Length
取如下地址返回到lookup
函数
e->val.fetch(xref, obj)
为Object.fecth
调用,但本质上是封装。调试发现find(key)
不为空,然后再次调用xref.fetch
,打印可以发现e
中的val
为XRef
对象
跟进验证上述说法,当type == objRef && xref
不为空时,调用xref->fetch
函数
跟进fetch
,调用了getObj
,完成循环调用
分析原因
产生这种洞的原因是通过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 | autoreconf -fvi |
静态编译,并指定生成路径
1 | ./configure --enable-shared=no --prefix="/home/fuzz/fuzzing_libexif/install" |
然后编译
1 | make |
构建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 | make |
测试
去网上找了个exif图片格式的图片仓库:https://github.com/ianare/exif-samples
使用 QEMU 模式执行模糊测试
添加软件源
1 | sudo vi /etc/apt/sources.list |
然后更新,安装最新版本的libc
1 | sudo apt update |
进行模糊测试
分析 crash
分析堆溢出
分析其中一个crash
直接运行后查看栈帧,发现在malloc_printer
中报了realloc(): invalid next size
的崩溃,然后在exif_entry_fix
处下断点
发现continue
12次发生崩溃,通过IDA反汇编查看函数exif_entry_fix
处的函数情况
此处调试循环,发现崩溃样例中的循环次数为0x66
次
同时由于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
抛出异常。
那么在循环结束时,调用这个函数,这个函数封装了realloc
函数
1 | e->data = (unsigned __int8 *)exif_entry_realloc(e, e->data, e->size); |
崩溃原因
查看glibc中realloc函数源码,可以发现由于堆溢出覆盖了堆上的数据(覆盖后几乎都是0,比较下列两张图),chunksize_nomask (next) <= CHUNK_HDR_SZ
满足条件,从而抛出异常
1 | next = chunk_at_offset (oldp, oldsize); |
(循环之前)
(经过循环之后)