入门一下fuzzing

简单安装配置

GitHub - AFLplusplus/AFLplusplus: The fuzzer afl++ is afl with community patches, qemu 5.1 upgrade, collision-free coverage, enhanced laf-intel & redqueen, AFLfast++ power schedules, MOpt mutators, unicorn_mode, and a lot more!

这里先不用源码编译的方式, 直接pull 一个镜像来尝尝鲜

docker pull aflplusplus/aflplusplus:latest

创建demo

创建一个目录

mkdir -p ~/afl-demo/in
cd ~/afl-demo

准备一个.c文件

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char *argv[]) {
FILE *fp;
char buf[32] = {0};

if (argc < 2) {
fprintf(stderr, "usage: %s <file>\n", argv[0]);
return 1;
}

fp = fopen(argv[1], "rb");
if (!fp) {
perror("fopen");
return 1;
}

size_t n = fread(buf, 1, 128, fp); // 故意读大一点,方便演示崩溃
fclose(fp);

if (n >= 4 && memcmp(buf, "FUZZ", 4) == 0) {
puts("found magic header");
}

return 0;
}

这个程序故意把最多128字节读进32字节缓冲区里, 所以很容易被 fuzz 出崩溃。

程序是从文件获取输入的, 所以再准备一个文件

printf 'AAAA' > in/seed.txt

启动

docker run -it --rm -v ~/afl-demo:/src aflplusplus/aflplusplus:latest

启动之后我们的文件都在/src下, 现在开始编译程序

cd /src
afl-cc -O0 -g demo.c -o demo

-O0 表示编译的时候不做优化, 跟gcc的参数是一样的

-g 生成调试符号

简单运行测试一下

./demo in/seed.txt

正常是不会有输出的, 也不会卡住什么的

开始fuzz

afl-fuzz -i in -o out -- ./demo @@

因为这个程序是从文件读输入, 所以命令里要用 @@

这是 AFL++ 官方 quick start 的标准写法:如果目标从文件读取,AFL++ 会把 @@ 替换成自动生成的测试用例路径

大概页面是这样, 说明已经在fuzzing了

它是不会自动停止的, 这里大概跑了11min, saved crashes 变为了1, 说明有一个样例导致这个程序崩溃了.

这时我们按下 ctrl + c就能停止, 导致程序crash的输入会存放在out/default/crashes/目录下

[AFL++ 8f4d96b5a055] /src # ls out/default/crashes
README.txt id:000000,sig:11,src:000001,time:271,execs:413,op:havoc,rep:7

README不用管, 我们直接看id:000000,sig:11,src:000001,time:271,execs:413,op:havoc,rep:7

[AFL++ 8f4d96b5a055] /src # cat out/default/crashes/id\:000000\,sig\:11\,src\:000001\,time\:271\,execs\:413\,op\:havoc\,rep\:7 
eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeGeeeeeeeeee

gdb一下

可以看到最终是在main函数返回之前的canary验证这里就死了
确实是溢出导致覆盖了canary

fuzzing 101 通关以及踩坑记录

1.Exercise 1 - Xpdf (Afl-clang-fast, Afl-fuzz, GDB)

复现一下 Vulnerability Details : CVE-2019-13288

下载目标程序并解压, 最好提前创建一个目录

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

正常构建(非必要)

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

这里下载些pdf来进行测试

cd $HOME/fuzzing_xpdf
mkdir pdf_examples && cd pdf_examples
wget https://github.com/mozilla/pdf.js-sample-files/raw/master/helloworld.pdf

测试一下

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

能正常使用, 说明安装没什么问题, 现在使用 afl-cc 和 afl-c++ 来编译安装xpdf. 先把之前的目标文件和可执行文件清理一下

rm -r $HOME/fuzzing_xpdf/install
cd /xpdf-3.02/ # 找到之前的路径
make clean

接着用afl特制编译器来再做一次编译安装的动作, 因为我是用docker镜像的, 所以稍微会有点不一样, 先启动一下

docker run -it --rm -v ~/afl-demo/fuzzing_xpdf:/src aflplusplus/aflplusplus:latest

然后进入之前clone的源码的目录下

cd /work/xpdf-3.02

用官方镜像的话就不需要更换llvm了

export CC=afl-cc
export CXX=afl-c++

配置并编译

./configure --prefix="$HOME/fuzzing_xpdf/install/"
make
make install

之前做测试的pdf被删了, 重新搞一下

cd $HOME/fuzzing_xpdf
mkdir pdf_examples && cd pdf_examples
wget https://github.com/mozilla/pdf.js-sample-files/raw/master/helloworld.pdf

因为教程中后面两个示例pdf都404了, 这里我们的初始语料库就只能有一个pdf. 可以非常直观的看到语料库对fuzz的影响.

返回上级目录, 然后开始fuzz

cd ..
afl-fuzz -i pdf_examples/ -o out/ -s 123 -- $HOME/fuzzing_xpdf/install/bin/pdftotext @@ /dev/null

等待

我这里跑了大概7分钟才跑出来第一个crash, 但是知乎的这个师傅只用了1分多钟

ok, 得到样例之后我们可以复现一下这个漏洞.

…中间出现了一点插曲, 不小心按错退出了docker, 因为在run的时候加了 –rm, 他直接就给我删掉了, out的路径还不是挂载的那个目录, 所以得再来一次. 建议还是把out放在挂载在宿主机的目录下

重新编译, 加上-g方便后续调试

rm -r $HOME/fuzzing_xpdf/install # 记得换成自己安装的目录
cd $HOME/fuzzing_xpdf/xpdf-3.02/
make clean
# 这里换成了挂载宿主机的目录了
CFLAGS="-g -O0" CXXFLAGS="-g -O0" ./configure --prefix="/src/work/xpdf/install/"
make
make install

然后gdb启动一下

gdb --args /src/work/xpdf/install/bin/pdftotext /src/work/out/default/crashes/id:000001
,sig:11,src:000968,time:365183,execs:452138,op:arith8,pos:459,val:-24 /dev/null

发现, 造成崩溃的原因跟 CVE-2019-13288 描述的不一样
这里应该是我的语料库太单调了, 只有一个 helloworld.pdf , 然后alf++ 对我的 helloworld.pdf 进行了疯狂的变异, 变了些神秘的16进制字符出来, 最后就死在了这个getchar()这, 既然这样, 我们就需要给语料库添加点素材

cd /in 
wget https://raw.githubusercontent.com/mozilla/pdf.js/master/test/pdfs/tracemonkey.pdf
wget https://raw.githubusercontent.com/mozilla/pdf.js/master/test/pdfs/160F-2019.pdf

然后依旧是编译启动, 这里就省略了
居然这么快就跑了一个crash出来… 最后是跑了大概一分钟, 总共3个crash
依旧是

rm -r $HOME/fuzzing_xpdf/install # 记得换成自己安装的目录
cd $HOME/fuzzing_xpdf/xpdf-3.02/
make clean
# 这里换成了挂载宿主机的目录了
CFLAGS="-g -O0" CXXFLAGS="-g -O0" ./configure --prefix="/src/work/xpdf/install/"
make
make install

然后就启动

gdb --args /src/work/xpdf/install/bin/pdftotext /src/work/out/default/crashes/id:000001,sig:11,src:000000,time:35363,execs:8962,op:havoc,rep:1   /dev/null

发现依旧复现不出来…
这次是触发了一个空指针异常, 感觉是我找的pdf过于现代了, 包含了对象流压缩结构这种比较新的东西, xpdf在解析对象的时候就 crash 了, 没法触发cve中所描述的无限递归导致的崩溃…

因为原课程中的另外两个pdf已经找不到了, 就没法很顺利的复现这个cve, 但不要紧, 个人认为能否复现cve不重要, 这个Exercise1 要我们做的就是熟悉Afl-clang-fast, Afl-fuzz, GDB而已, 我也确实做到了

Exercise 2 - libexif (Afl-clang-lto, Fuzz libraries, Eclipse IDE)

施工ing