Exercise 1 - Xpdf

Exercise 1 - Xpdf

一、实验背景

CVE-2019-13288是一个可能通过精心制作的文件导致无线递归的漏洞

程序中每个被调用的函数都在堆栈上分配一个堆栈帧,如果一个函数被递归调用多次,可能会导致堆栈内存耗尽和程序崩溃,因此,远程攻击者可以利用该漏洞进行Dos攻击

非受控制递归漏洞(https://cwe.mitre.org/data/definitions/674.html)

二、实验目的

模糊Xpfd(PDF查看器),目标是在XPDF 3.02中找到CVE-2019-13288漏洞

三、实验环境:

win10,VMware,Ubuntu 20.04.2 LTS(虚拟机用户名密码均为fuzz)

四、实验步骤

1、下载并构建目标

得到模糊目标,为要模糊化的项目创建目录

1
2
cd $HOME
mkdir fuzzing_xpdf && cd fuzzing_xpdf/

image

安装make和gcc,准备好环境,注意要先更新apt-get,否则会出现错误Unable to locate package build-essential

1
2
sudo apt-get update 
sudo apt install build-essential

image

image

下载Xpdf 3.02压缩包并解压

1
2
wget https://dl.xpdfreader.com/old/xpdf-3.02.tar.gz
tar -xvzf xpdf-3.02.tar.gz

image

构建Xpdf

1
2
3
4
5
cd xpdf-3.02
sudo apt update && sudo apt install -y build-essential gcc
./configure --prefix="$HOME/fuzzing_xpdf/install/"
make
make install

image

image

image

image

下载一些PDF示例用于后续验证

下载pdf时注意在后面加参数–no-check-certificate跳过验证证书,否则会报错unable to establish ssl connection.

1
2
3
4
5
cd $HOME/fuzzing_xpdf
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 –no-check-certificate
wget https://www.melbpc.org.au/wp-content/uploads/2017/10/small-example-pdf-file.pdf

image

image

测试pdfinfo二进制文件

1
$HOME/fuzzing_xpdf/install/bin/pdfinfo -box -meta $HOME/fuzzing_xpdf/pdf_examples/helloworld.pdf

看到如下页面说明验证成功

image

2、安装AFL++

采用本地安装的方式,首先安装依赖项

1
2
3
4
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/\..*//')-dev

image

image

image

构造AFL++

1
2
3
4
5
cd $HOME
git clone https://github.com/AFLplusplus/AFLplusplus && cd AFLplusplus
export LLVM_CONFIG="llvm-config-11"
make distrib
sudo make install

image-20230726155938884

image

Docker image

安装docker,第一次安装后无法使用docker,使用snap重新下载后可以使用

1
sudo apt install docker

image

image

Pull图像,要加上sudo以管理员身份运行,否则会报错connect: permission denied

1
docker pull aflplusplus/aflplusplus

image-20230726161946524

image

启动AFLPlusPlus docker容器

1
docker run -ti -v $HOME:/home aflplusplus/aflplusplus

再运行

1
export $HOME="/home"

输入afl-fuzz运行afl-fuzz,出现下图说明运行成功

image

3、开始实验

为了使我们的目标应用程序启用插装,我们需要使用AFL的编译器编译代码,首先,我们要清理所有之前编译过的目标文件和可执行文件

1
2
3
rm -r $HOME/fuzzing_xpdf/install
cd $HOME/fuzzing_xpdf/xpdf-3.02/
make clean

image

使用afl-clang-fast编译器构建xpdf

1
2
3
4
export LLVM_CONFIG="llvm-config-11"
CC=$HOME/AFLplusplus/afl-clang-fast CXX=$HOME/AFLplusplus/afl-clang-fast++ ./configure --prefix="$HOME/fuzzing_xpdf/install/"
make
make install

image

image

image

每个选项的简要说明:

  • -i 表示我们必须放置输入大小写的目录(即文件示例)
  • -o 表示afl++存储修改后文件的目录
  • -s 表示使用静态随机种子
  • @@ 是AFL将用每个输入文件名替换的占位符目标的命令行

fuzzer基本上会运行下面这个命令,用于每个不同的输入文件

1
2
$HOME/fuzzing_xpdf/install/bin/pdftotext <input-file-name>
$HOME/fuzzing_xpdf/output

使用以下命令运行fuzzer,出现错误如下图

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

image

如果收到消息“Hmm, your system is configured to send core dump notifications to an external utility…”,只要这样做

1
2
3
sudo su
echo core >/proc/sys/kernel/core_pattern
exit

image

再运行fuzzer,几分钟后,可以看到如下页面

image

看到saved crashes的值,是崩溃的数量

可以在“$HOME/fuzzing_xpdf/out/”目录中找到这些崩溃文件

image

4、调试和分类

通过调试和分类,来找到关于这个bug的信息

1)用指定的文件重现崩溃
2)调试崩溃找出问题所在
3)修复问题

在’ $HOME/fuzzing_xpdf/out/ ‘目录中找到与崩溃对应的文件。文件名类于“id:000000,sig:11,src:000526,time:534057,execs:257582,op:havoc,rep:8”

image

将此文件作为输入传递给pdftotext二进制文件

1
$HOME/fuzzing_xpdf/install/bin/pdftotext '$HOME/fuzzing_xpdf/out/default/crashes/<your_filename>' $HOME/fuzzing_xpdf/output

它将导致分割错误,并导致程序崩溃

image

使用gdb找出使用此输入导致程序崩溃的原因

GDB的简单入门(http://people.cs.pitt.edu/~mosse/gdb-note.html)

首先,使用调试信息重新构建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

image

image

image

image

运行GDB

1
2
3
gdb --args $HOME/fuzzing_xpdf/install/bin/pdftotext $HOME/fuzzing_xpdf/out/default/crashes/id:000000,sig:11,src:000526,time:534057,execs:257582,op:havoc,rep:8 $HOME/fuzzing_xpdf/output

>> run

image

正常情况下可看到如下输出

看到Program received signal SIGSEGV, Segmentation fault,说明存在内存泄漏

报错位置是in _int_malloc (av=av@entry=0x7ffff7c63b80 , bytes=bytes@entry=4) at malloc.c:3545,glibc报了个错,显然是堆内存出了问题

image

再输入bt获取回溯

image

滚动调用堆栈,可以看到许多对“Parser::getObj”方法的调用,这些调用似乎表示无限递归。访问https://www.cvedetails.com/cve/CVE-2019-13288/可以看到描述从GDB获得的回溯匹配。

执行流信息,分析一下可以看出调用过程是循环的,判断为无限循环漏洞

根据函数调用找到漏洞位置

从fuzzing_xpdf/xpdf-3.02/xpdf/Parse.cc 94行的makeStream调用,一路跟着报错往下翻就会找到这个套娃

image

5、修复问题

方法

  1. 修复后重建目标,并检查用例是否不再导致分段错误
  2. 安装错误已经修复的Xpdf 4.02,并检查分段错误是否消失

我选择的是安装已修复的Xpdf 4.02,对比源代码,发现再Xpdf 4.02的文件Parse.cc中,加了个变量,记录循环次数,超过一定次数就结束进程

1
2
wget https://dl.xpdfreader.com/old/xpdf-4.02.tar.gz
tar -xvzf xpdf-4.02.tar.gz

image

image

五、总结

通过这个实验,我了解了CVE-2019-13288漏洞,学习了**AFL++**,如何使用带有插装的afl编译器编译目标,如何启动afl++,如何检测目标崩溃。

image

界面信息介绍

process timing:执行时间信息

run time:运行总时间

last new find:距离最近一次发现新路径的时间

last saved crash:距离最近一次保存程序崩溃的时间

last saved hang:距离最近一次保存挂起的时间

overall results:

cycles done:运行的总周期数

corpus count:语料库计数

saved crashes:保存的程序崩溃个数

saved hang:保存的挂起个数

cycle progress:

now processing:当前的测试用例ID(所在输入队列的位置)

runs timed out:超时数量

map coverage:覆盖率

map density:目前已经命中多少分支元组,与位图可以容纳多少的比例

count coverage:位图中每个被命中的字节平均改变的位数

stage progress:

now trying: 指明当前所用的变异输入的方法

stage execs: 当前阶段的进度指示

total execs: 全局的进度指示

exec speed: 执行速度

findings in depth:种子变异产生的信息

favored items: 基于最小化算法产生新的更好的路径

new edges on: 基于更好路径产生的新边

total crashes: 基于更好路径产生的崩溃

total tmouts: 基于更好路径产生的超时 包括所有超时的超时

fuzzing strategy yields: 进一步展示了AFL所做的工作,在更有效路径上得到的结果比例,对应上面的now trying

bit flips: 比特位翻转,例如:

bitflip 1/1,每次翻转1个bit,按照每1个bit的步长从头开始

bitflip 2/1,每次翻转相邻的2个bit,按照每1个bit的步长从头开始

byte flips: 字节翻转

arithmetics: 算术运算,例如:

arith 16/8,每次对16个bit进行加减运算,按照每8个bit的步长从头开始,即对文件的每个word进行整数加减变异

know ints: 用于替换的基本都是可能会造成溢出的数,例:

interest 16/8,每次对16个bit进替换,按照每8个bit的步长从头开始,即对文件的每个word进行替换

dictionary: 有以下子阶段:

user extras (over),从头开始,将用户提供的tokens依次替换到原文件中

user extras (insert),从头开始,将用户提供的tokens依次插入到原文件中

auto extras (over),从头开始,将自动检测的tokens依次替换到原文件中

其中,用户提供的tokens,是在词典文件中设置并通过-x选项指定的,如果没有则跳过相应的子阶段。

havoc:顾名思义,是充满了各种随机生成的变异,是对原文件的“大破坏”。具体来说,havoc包含了对原文件的多轮变异,每一轮都是将多种方式组合(stacked)而成

splice:在任意选择的中点将队列中的两个随机输入拼接在一起.

py/custom/req:

trim:修建测试用例使其更短,但保证裁剪后仍能达到相同的执行路径

eff

item geometry:

levels: 表示测试等级

pending: 表示还没有经过fuzzing的输入数量

pend fav: 表明fuzzer感兴趣的输入数量

own finds: 表示在fuzzing过程中新找到的,或者是并行测试从另一个实例导入的数量

imported: n/a表明不可用,即没有导入

stability: 表明相同输入是否产生了相同的行为,一般结果都是100%

参考博客:https://blog.csdn.net/qq_40025866/article/details/127823491