fuzz101 Exercise

henry Lv4

以下所有实验都在 Ubuntu20.04 所做,环境搭建可以直接参考 Fuzzing101 中的 README 文件。

1
git clone https://github.com/antonio-morales/Fuzzing101

Exercise 1:Xpdf

Xpdf是一款免费的PDF查看器和工具包,包括文本提取器、图像转换器、HTML转换器等。大部分工具都是开源的。

CVE-2019-13288 is a vulnerability that may cause an infinite recursion via a crafted file.

Since each called function in a program allocates a stack frame on the stack, if a a function is recursively called so many times it can lead to stack memory exhaustion and program crash.

As a result, a remote attacker can leverage this for a DoS attack.

You can find more information about Uncontrolled Recursion vulnerabilities at the following link: https://cwe.mitre.org/data/definitions/674.html

知识点

  • 编译一个目标程序
  • 学会使用 afl-fuzz
  • 通过 gdb 调试 fuzz 出来的 crash 文件

分析

搭建Xpdf环境:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
cd $HOME
mkdir fuzzing_xpdf && cd fuzzing_xpdf/

# 相关依赖
sudo apt install build-essential
sudo apt update && sudo apt install -y build-essential gcc

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

# Download PDF examples to test Xpdf
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
wget https://www.melbpc.org.au/wp-content/uploads/2017/10/small-example-pdf-file.pdf

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

测试 xpdf 功能

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

nipaste_2024-07-04_11-24-4

搭建fuzz环境

1
2
3
4
5
6
7
8
9
10
11
12
# install dependency
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

# Build AFL++
cd $HOME
git clone https://github.com/AFLplusplus/AFLplusplus && cd AFLplusplus
export LLVM_CONFIG="llvm-config-11"
make distrib
sudo make install

使用afl-clang-fastXpdf进行插桩:

1
2
3
4
5
6
7
8
9
10
# clean all previously compiled object files and executables
rm -r $HOME/fuzzing_xpdf/install
cd $HOME/fuzzing_xpdf/xpdf-3.02/
make clean

# compile with afl-clang-fast
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

参数设置如下:

  • -i:设置输入实例的文件夹
  • -o:设置用于存放模糊测试结果的文件夹
  • -s:设置一个静态随机数作为种子
  • --:设置测试目标
  • @@:占位符,指代每一个输入的文件,告诉 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

然后就可以开始正常 fuzz了,边看源码边等,或者像我一样直接摆烂。

nipaste_2024-07-04_14-41-4

四十分钟之后爆了10个crash

nipaste_2024-07-04_15-35-3

设置gdb调试环境,这里我们把插桩后的xpdf程序重新删掉,然后正常编译,利用crash里面的文件,来分析程序crash的原因

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/xpdf-3.02/xpdf/pdftotext $HOME/fuzzing_xpdf/out/default/crashes/id:000000,sig:11,src:000748,time:352834,execs:158296,op:havoc,rep:4 $HOME/fuzzing_xpdf/output

nipaste_2024-07-04_15-52-0

程序一直在重复输出错误信息,直到最后出现 Segmentation fault

nipaste_2024-07-04_16-34-4

输出bt,来查看函数的调用栈,我们来定位到源码文件中来分析原因。

nipaste_2024-07-04_16-33-3

这里出现了一个无限递归,依次提取出递归链

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// Parser.cc
Object *Parser::getObj(Object *obj, Guchar *fileKey,
CryptAlgorithm encAlgorithm, int keyLength,
int objNum, int objGen) {
//...
if ((str = makeStream(obj, fileKey, encAlgorithm, keyLength, objNum, objGen))) {
//...

Stream *Parser::makeStream(Object *dict, Guchar *fileKey,
CryptAlgorithm encAlgorithm, int keyLength,
int objNum, int objGen) {
//...
dict->dictLookup("Length", &obj);
//...
}

// Object.h
inline Object *Object::dictLookup(char *key, Object *obj)
{ return dict->lookup(key, obj); }

// Dict.cc
Object *Dict::lookup(char *key, Object *obj) {
DictEntry *e;
return (e = find(key)) ? e->val.fetch(xref, obj) : obj->initNull();
}

// Object.cc
Object *Object::fetch(XRef *xref, Object *obj) {
return (type == objRef && xref) ?
xref->fetch(ref.num, ref.gen, obj) : copy(obj);
}
// XRef.cc
Object *XRef::fetch(int num, int gen, Object *obj){
//...
parser->getObj(obj, encrypted ? fileKey : (Guchar *)NULL,
encAlgorithm, keyLength, num, gen);
//...
}

至此我们就搞明白了出现无限递归的原因,然后再来看看4.00 版本中是如何处理这一 bug 的,这里diff一下这两个文件来看是怎么解决这一问题的。

nipaste_2024-07-04_16-43-1

这里是直接设置了一个 recursionLimit 宏变量来设置递归的次数,从而防止程序一直递归。

总结

我们几乎没有对xpdf的底层原理进行详细了解,只了解其简单功能,通过 AFL 对 xpdf 进行插桩,就 fuzz 出了 CVE-2019-13288 ,可见fuzz威力的强大,不过 fuzz 的功能还要比这更丰富。

拓展

由于有好几个crashes错误,依次来简单看一下

id:000001,sig:11,src:000001,time:453622,execs:197479,op:havoc,rep:16

nipaste_2024-07-04_17-01-5

nipaste_2024-07-04_17-02-4

报错信息显示pdf文件已被损坏,很显然这里是一个非法地址引用的错误,很不幸运的是后面所有的crash基本上与这个都是相同的报错类型。

Exercise 2:libexif

libexif 是一个用于解析、编辑和保存 EXIF 数据的库。它的设计目的是替换许多命令行工具和带图形用户界面(GUI)的程序中冗余的实现。

CVE-2009-3895 is a heap-based buffer overflow that can be triggered with an invalid EXIF image.

A heap-based buffer overflow is a type of buffer overflow that occurs in the heap data area, and it’s usually related to explicit dynamic memory management (allocation/deallocation with malloc() and free() functions).

As a result, a remote attacker can exploit this issue to execute arbitrary code within the context of an application using the affected library.

You can find more information about Heap-based buffer oveflow vulnerabilities at the following link: https://cwe.mitre.org/data/definitions/122.html


CVE-2012-2836 is an Out-of-bounds Read vulneratibily that can be triggered via an image with crafted EXIF tags.

An Out-of-bounds Read is a vulnerability that occurs when the program reads data past the end, or before the beginning, of the intended buffer.

As a result, it allows remote attackers to cause a denial of service or possibly obtain potentially sensitive information from process memory.

You can find more information about Out-of-bounds Read vulnerabilities at the following link: https://cwe.mitre.org/data/definitions/125.html

知识点

  • 使用一个外部程序exif 去 fuzz libexif库

环境搭建

下载并编译目标文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
cd $HOME
mkdir fuzzing_libexif && cd fuzzing_libexif/


# Download and uncompress libexif-0.6.14:
wget https://github.com/libexif/libexif/archive/refs/tags/libexif-0_6_14-release.tar.gz
tar -xzvf libexif-0_6_14-release.tar.gz

# Build and install libexif
cd libexif-libexif-0_6_14-release/
sudo apt-get install autopoint libtool gettext libpopt-dev
autoreconf -fvi
./configure --enable-shared=no --prefix="$HOME/fuzzing_libexif/install/"
make
make install

选择一个应用程序

因为 libexif 是一个库,我们并不能直接使用它,而是需要一个调用该库的应用程序,并且选择该程序为fuzz目标,这里下载使用 exif 命令行程序。

1
2
3
4
5
6
7
8
9
10
11
cd $HOME/fuzzing_libexif
wget https://github.com/libexif/exif/archive/refs/tags/exif-0_6_15-release.tar.gz
tar -xzvf exif-0_6_15-release.tar.gz

# build and install exif command-line utility

cd exif-exif-0_6_15-release/
autoreconf -fvi
./configure --enable-shared=no --prefix="$HOME/fuzzing_libexif/install/" PKG_CONFIG_PATH=$HOME/fuzzing_libexif/install/lib/pkgconfig
make
make install

测试命令

1
$HOME/fuzzing_libexif/install/bin/exif

nipaste_2024-07-05_10-07-2

下载语料库

1
2
3
4
# get some exif samples
cd $HOME/fuzzing_libexif
wget https://github.com/ianare/exif-samples/archive/refs/heads/master.zip
unzip master.zip

测试用例

1
$HOME/fuzzing_libexif/install/bin/exif $HOME/fuzzing_libexif/exif-samples-master/jpg/Canon_40D_photoshop_import.jpg

nipaste_2024-07-05_10-14-4

可以看到成功通过 exif 命令行程序输出了图片中的 exif 信息

使用 Afl-clang-lto 对 libexif 和 exif 进行编译插桩

1
2
3
4
5
6
7
8
9
10
11
12
13
14
rm -r $HOME/fuzzing_libexif/install
cd $HOME/fuzzing_libexif/libexif-libexif-0_6_14-release/
make clean
export LLVM_CONFIG="llvm-config-11"
CC=afl-clang-lto ./configure --enable-shared=no --prefix="$HOME/fuzzing_libexif/install/"
make
make install

cd $HOME/fuzzing_libexif/exif-exif-0_6_15-release
make clean
export LLVM_CONFIG="llvm-config-11"
CC=afl-clang-lto ./configure --enable-shared=no --prefix="$HOME/fuzzing_libexif/install/" PKG_CONFIG_PATH=$HOME/fuzzing_libexif/install/lib/pkgconfig
make
make install

afl-clang-lto 支持无碰撞,并且拥有比 afl-clang-fast 更快地速度。

1
afl-fuzz -i $HOME/fuzzing_libexif/exif-samples-master/jpg/ -o $HOME/fuzzing_libexif/out/ -s 123 -- $HOME/fuzzing_libexif/install/bin/exif @@

注意事项

nipaste_2024-07-05_10-48-3

然而我发现我安装的 afl++ 中并没有 afl-clang-lto,所以我这里还是依然采用 afl-clang-fast 来进行编译插桩,把上面的 CC=afl-clang-lto 改成 CC=afl-clang-fast就好了。

然后就可以开始正常 fuzz,秒出了一个,不知道有没有用,待会一块看。

nipaste_2024-07-05_10-51-3

跑了大概三十分钟左右出了大概10个crashes,忍不住了直接看看有没有比较有用的crash。

nipaste_2024-07-05_11-21-4

对源码重新编译不使用afl++

1
2
3
4
5
6
7
8
9
10
cd libexif-libexif-0_6_14-release/
./configure --enable-shared=no --prefix="$HOME/fuzzing_libexif/install/"
make
make install


cd exif-exif-0_6_15-release/
./configure --enable-shared=no --prefix="$HOME/fuzzing_libexif/install/" PKG_CONFIG_PATH=$HOME/fuzzing_libexif/install/lib/pkgconfig
make
make install

gdb设置调试

1
gdb --args $HOME/fuzzing_libexif/install/bin/exif out/default/crashes/$1 $HOME/fuzzing_libexif/out

$1 设置为 crashes 文件夹中的任意一个就可以了

分析

CVE-2012-2836

id:000000,sig:11,src:000005,time:17828,execs:6742,op:arith32,pos:34,val:be:-9

nipaste_2024-07-05_14-09-0

函数调用栈

1
2
3
4
5
6
► f 0   0x5555555682e0 exif_get_sshort+48
f 1 0x555555563096 exif_data_load_data+726
f 2 0x555555563096 exif_data_load_data+726
f 3 0x55555556775a exif_loader_get_data+58
f 4 0x55555555e0e7 main+2407
f 5 0x7ffff7c85083 __libc_start_main+243

这个漏洞看起来很有意思,因为漏洞发生在 exif 本身这个程序当中,而且存在一个非法地址引用,可以通过调试来探究一下具体原因。

1
2
3
4
5
6
7
#0  exif_get_sshort (buf=0x55565558af75 <error: Cannot access memory at address 0x55565558af75>, order=EXIF_BYTE_ORDER_MOTOROLA) at exif-utils.c:87
#1 0x0000555555563096 in exif_data_load_data (ds_orig=<optimized out>, d_orig=<optimized out>, data=0x55555558b400) at exif-data.c:819
#2 exif_data_load_data (data=data@entry=0x55555558b400, d_orig=<optimized out>, ds_orig=<optimized out>) at exif-data.c:698
#3 0x000055555556775a in exif_loader_get_data (loader=0x55555558af20) at exif-loader.c:387
#4 0x000055555555e0e7 in main (argc=argc@entry=3, argv=argv@entry=0x7fffffffe318) at main.c:438
#5 0x00007ffff7c85083 in __libc_start_main (main=0x55555555d780 <main>, argc=3, argv=0x7fffffffe318, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffffffe308) at ../csu/libc-start.c:308
#6 0x000055555555eace in _start ()

先来搞清楚 exif_get_sshort 是干嘛用的,这是 exif_get_sshort 的源码内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
ExifSShort
exif_get_sshort (const unsigned char *buf, ExifByteOrder order)
{
if (!buf) return 0;
switch (order) {
case EXIF_BYTE_ORDER_MOTOROLA:
return ((buf[0] << 8) | buf[1]);
case EXIF_BYTE_ORDER_INTEL:
return ((buf[1] << 8) | buf[0]);
}

/* Won't be reached */
return (0);
}

exif_get_sshort函数从给定的EXIF数据缓冲区中提取一个16位的短整型数值,并根据指定的字节顺序进行解释

nipaste_2024-07-05_14-42-0

在发生crash的函数处下个断点,可以发现在调用 exif_get_short 时,已经传递了一个非法地址才会造成crash,一步一步往前推试着搞明白这个非法地址是怎么来的

nipaste_2024-07-05_14-45-0

这一步已经开始发生错误了,rdx 为 0xffffffff 导致在加了一个堆地址之后直接溢出了,再往前看看

nipaste_2024-07-05_14-47-2

这里有一个边界检查,但由于 r13 为 0xffffffff 在 +8 之后整数溢出变为了7,因此成功绕过判断,再往前找找

nipaste_2024-07-05_14-52-5

1
2
3
► 0x55555556302e <exif_data_load_data+622>    call   exif_get_long                <exif_get_long>
rdi: 0x55555558af7a ◂— 0xf010c00ffffffff
rsi: 0x0

可以发现在 exif_get_long 这一步,给offset 的返回值为 0xffffffff,罪魁祸首就是 exif_get_long 这里,来看看这个函数的功能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
ExifLong
exif_get_long (const unsigned char *buf, ExifByteOrder order)
{
return (exif_get_slong (buf, order) & 0xffffffff);
}


ExifSLong
exif_get_slong (const unsigned char *b, ExifByteOrder order)
{
if (!b) return 0;
switch (order) {
case EXIF_BYTE_ORDER_MOTOROLA:
return ((b[0] << 24) | (b[1] << 16) | (b[2] << 8) | b[3]);
case EXIF_BYTE_ORDER_INTEL:
return ((b[3] << 24) | (b[2] << 16) | (b[1] << 8) | b[0]);
}

/* Won't be reached */
return (0);
}

由于低四字节为 0xffffffff,所以实际上在经过移位异或之后依然是 0xffffffff,前面说过我们的调用栈如下

1
2
3
4
5
6
► f 0   0x5555555682e0 exif_get_sshort+48
f 1 0x555555563096 exif_data_load_data+726
f 2 0x555555563096 exif_data_load_data+726
f 3 0x55555556775a exif_loader_get_data+58
f 4 0x55555555e0e7 main+2407
f 5 0x7ffff7c85083 __libc_start_main+243

我们现在的目的就是找到 0xf010c00ffffffff何时被加载到程序当中

nipaste_2024-07-05_16-18-4

从上图可以看到 exif_loader_get_data 传递的 rdi 即为 loader 指针,而 loader->buf 位于 loader+0x20 的位置处,这里就有 0xf010c00ffffffff

nipaste_2024-07-05_16-18-0

nipaste_2024-07-05_16-24-0

从上面的信息可以知道,程序从 main 函数在进入 exif_data_load_data 时,其数据已经被污染,结合源码在进一步调试分析

nipaste_2024-07-05_16-30-5

这里根据所给参数,将从 jpg 文件中读取 exif 数据到 exif_loader 结构体中。

nipaste_2024-07-05_16-32-2

可以看到这里的文件是从我们之前fuzz的crash中读入的,读入之后的loader结构体初始化完成,可以看到0xf010c00ffffffff最终来自于这里,即图片文件的 exif 数据部分,而这一部分我们完全可以伪造该数据,从而使目标程序崩溃。

nipaste_2024-07-05_16-33-5

事实上,该漏洞即为 CVE-2012-2836 ,fuzz 确实在一定程序上可以帮助我们快速定位漏洞。

CVE-2009-3895

id:000001,sig:11,src:000010,time:257915,execs:74652,op:havoc,rep:5

再来看看另外一个 crash,也是存在一个非法地址引用,但是函数调用栈不同

nipaste_2024-07-05_14-01-3

调用栈如下所示

1
2
3
4
5
6
7
► f 0   0x7ffff7decb38 __memmove_avx_unaligned_erms+552
f 1 0x5555555629ba exif_data_load_data_content+906
f 2 0x555555563115 exif_data_load_data+853
f 3 0x555555563115 exif_data_load_data+853
f 4 0x55555556775a exif_loader_get_data+58
f 5 0x55555555e0e7 main+2407
f 6 0x7ffff7c85083 __libc_start_main+243
1
2
3
4
5
6
7
8
#0  __memmove_avx_unaligned_erms () at ../sysdeps/x86_64/multiarch/memmove-vec-unaligned-erms.S:440
#1 0x00005555555629ba in exif_data_load_data_content (data=data@entry=0x55555558b750, ifd=ifd@entry=EXIF_IFD_1, d=d@entry=0x55555558af66 "II*", ds=ds@entry=2011, offset=924, offset@entry=922, recursion_depth=recursion_depth@entry=0) at exif-data.c:381
#2 0x0000555555563115 in exif_data_load_data (ds_orig=<optimized out>, d_orig=<optimized out>, data=0x55555558b750) at exif-data.c:835
#3 exif_data_load_data (data=data@entry=0x55555558b750, d_orig=<optimized out>, ds_orig=<optimized out>) at exif-data.c:698
#4 0x000055555556775a in exif_loader_get_data (loader=0x55555558af10) at exif-loader.c:387
#5 0x000055555555e0e7 in main (argc=argc@entry=3, argv=argv@entry=0x7fffffffe328) at main.c:438
#6 0x00007ffff7c85083 in __libc_start_main (main=0x55555555d780 <main>, argc=3, argv=0x7fffffffe328, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffffffe318) at ../csu/libc-start.c:308
#7 0x000055555555eace in _start ()

根据上面的信息定位到最后触发漏洞的位置,即源码 exif-data.c:381行的 exif_data_load_data_thumbnail

nipaste_2024-07-06_14-42-4

下个断点尝试找到这里

nipaste_2024-07-06_14-47-3

可以明显发现这里 memcpy 的第三个参数过大,回导致后面复制过程中出现非法地址,随即让程序crash

nipaste_2024-07-06_14-49-2

回到 exif_data_load_data_thumbnail 的源码分析,如果size设置过大的话,这里会不通过检查,那我们看看这个判断是如何绕过的

nipaste_2024-07-06_14-53-3

可以发现这里由于 size 为 0xfffffd25 所以导致了 32位整数溢出,从而绕过了判断。

nipaste_2024-07-06_14-55-1

接下来就好分析了,只需要知道是哪一步设置了 size,从而就知道了这个触发这个漏洞的所在地

nipaste_2024-07-06_15-09-4

nipaste_2024-07-06_15-10-1

返回值即为上面的 0xfffffd25,该数据来自一个堆块中,一直往前跟就会找到答案,这部分堆块数据是从照片中提取出的 Exif data 信息,因而使得可以控制这一数据来实现堆块越界读的效果。

nipaste_2024-07-06_15-22-0

至此完成了对 CVE-2009-3895 的分析。

id:000007,sig:11,src:000476,time:972518,execs:289326,op:int32,pos:852,val:-1

与上一个调用栈不同,但是漏洞触发的地点是一样的,应该都是同一原因导致的。

nipaste_2024-07-05_14-06-1

拓展

挖个坑,回头尝试对最新版本的 libexif 库进行 fuzz。

Exercise 3:TCPdump

TCPdump是一个网络数据包分析工具,它可以截获网络中传送的数据包,并提供详细的分析功能

CVE-2017-13028 is an Out-of-bounds Read vulneratibily that can be triggered via a BOOTP packet (Bootstrap Protocol).

As a result, it allows remote attackers to cause a denial of service or possibly obtain potentially sensitive information from process memory.

You can find more information about Out-of-bounds Read vulnerabilities at the following link: https://cwe.mitre.org/data/definitions/125.html

知识点

  • 了解 ASan(Address Sanitizer),这是一个实时内存检测工具
  • 学会使用 ASAN 去 fuzz 一个目标
  • 使用 ASAN 触发 crash

环境搭建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 创建目录
cd $HOME
mkdir fuzzing_tcpdump && cd fuzzing_tcpdump/

# 下载解压 tcpdump-4.9.2.tar.gz
wget https://github.com/the-tcpdump-group/tcpdump/archive/refs/tags/tcpdump-4.9.2.tar.gz
tar -xzvf tcpdump-4.9.2.tar.gz

# 下载解压 libpcap,这个库是 TCPdump 运行所需要的
wget https://github.com/the-tcpdump-group/libpcap/archive/refs/tags/libpcap-1.8.0.tar.gz
tar -xzvf libpcap-1.8.0.tar.gz

# 将 libpcap-libpcap-1.8.0 重命名为 libpcap-1.8.0 否则, tcpdump 找不到 libpcap的路径
mv libpcap-libpcap-1.8.0/ libpcap-1.8.0

# 编译安装 libpcap
cd $HOME/fuzzing_tcpdump/libpcap-1.8.0/
./configure --enable-shared=no
make

# 编译安装 tcpdump
cd $HOME/fuzzing_tcpdump/tcpdump-tcpdump-4.9.2/
./configure --prefix="$HOME/fuzzing_tcpdump/install/"
make
make install

测试命令

$HOME/fuzzing_tcpdump/install/sbin/tcpdump -h

$HOME/fuzzing_tcpdump/install/sbin/tcpdump -vvvvXX -ee -nn -r ./tests/geneve.pcap

nipaste_2024-07-06_16-08-3

出现如图所示的界面,就说明功能正常

AddressSanitizer

AddressSanitizer (ASan) 是针对 C 和 C++ 的一个快速内存错误检测器,在 2011年5月,由 Google (Konstantin Serebryany, Derek Bruening, Alexander Potapenko, Dmitry Vyukov) 发布。

ASan 由一个编译插桩模块和实时运行库组成,该工具可以找到堆、栈、全局对象中的越界访问错误,如 UAF,double-free 和 内存泄露。

ASan 编译插桩目标

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 删除原来的环境
rm -r $HOME/fuzzing_tcpdump/install
cd $HOME/fuzzing_tcpdump/libpcap-1.8.0/
make clean

cd $HOME/fuzzing_tcpdump/tcpdump-tcpdump-4.9.2/
make clean

# 在调用 configure 和 make 前,设置 AFL_USE_ASAN=1
cd $HOME/fuzzing_tcpdump/libpcap-1.8.0/
export LLVM_CONFIG="llvm-config-11"
CC=afl-clang-fast ./configure --enable-shared=no --prefix="$HOME/fuzzing_tcpdump/install/"
AFL_USE_ASAN=1 make

cd $HOME/fuzzing_tcpdump/tcpdump-tcpdump-4.9.2/
AFL_USE_ASAN=1 CC=afl-clang-fast ./configure --prefix="$HOME/fuzzing_tcpdump/install/"
AFL_USE_ASAN=1 make
AFL_USE_ASAN=1 make install

然后就可以使用下面的命令开始进行 fuzz 了

afl-fuzz -m none -i $HOME/fuzzing_tcpdump/tcpdump-tcpdump-4.9.2/tests/ -o $HOME/fuzzing_tcpdump/out/ -s 222 -- $HOME/fuzzing_tcpdump/install/sbin/tcpdump -vvvvXX -ee -nn -r @@

nipaste_2024-07-06_16-30-5

分析

大概在跑了20小时之后,仅出了一个 crash,由于之前编译的时候使用了 ASAN,所以就不需要重新编译整个文件进行调试了。

1
$HOME/fuzzing_tcpdump/install/sbin/tcpdump -vvvvXX -ee -nn -r $HOME/fuzzing_tcpdump/out/default/crashes/id:000000,sig:06,src:000039,time:27796493,execs:2488857,op:havoc,rep:7

nipaste_2024-07-07_16-04-5

这里报了一个堆溢出的错误,可以在这里找到 CVE-2017-13028 的详细补丁信息,由于这种溢出类错误可能不会导致程序直接 crash,所以跟踪调试起来稍微麻烦点,后期再来看看。

总结

对于这种堆栈溢出不会直接造成程序崩溃的 poc,我们需要如何从调试来具体分析原因,或许可以从ASAN中的函数调用栈来进行跟踪

Exercise 4:LibTIFF

libTIFF是一个用于处理TIFF(Tagged Image File Format,标记图像文件格式)图像文件的开源软件库。TIFF是一种常见的图像文件格式,主要用于存储照片和图像。

CVE-2016-9297 is an Out-of-bounds Read vulnerability that can be triggered via crafted TIFF_SETGET_C16ASCII or TIFF_SETGET_C32_ASCII tag values.

An Out-of-bounds Read is a vulnerability that occurs when the program reads data past the end, or before the beginning, of the intended buffer.

As a result, it allows remote attackers to cause a denial of service or possibly obtain potentially sensitive information from process memory.

You can find more information about Out-of-bounds Read vulnerabilities at the following link: https://cwe.mitre.org/data/definitions/125.html

知识点

  • 如何使用代码覆盖率数据来提高 fuzz 性能
  • 使用 lcov(覆盖率检测)编译libtiff

环境搭建

1
2
3
4
5
6
7
8
9
10
11
12
### Download and build your target
cd $HOME
mkdir fuzzing_tiff && cd fuzzing_tiff/

# Download and uncompress libtiff 4.0.4:
wget https://download.osgeo.org/libtiff/tiff-4.0.4.tar.gz
tar -xzvf tiff-4.0.4.tar.gz

cd tiff-4.0.4/
./configure --prefix="$HOME/fuzzing_tiff/install/" --disable-shared
make
make install

$HOME/fuzzing_tiff/install/bin/tiffinfo -D -j -c -r -s -w $HOME/fuzzing_tiff/tiff-4.0.4/test/images/palette-1c-1b.tiff

测试功能是否能正常运行

nipaste_2024-07-07_16-26-0

使用功能见下表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
usage: tiffinfo [options] input...
where options are:
-D read data
-i ignore read errors
-c display data for grey/color response curve or colormap
-d display raw/decoded image data
-f lsb2msb force lsb-to-msb FillOrder for input
-f msb2lsb force msb-to-lsb FillOrder for input
-j show JPEG tables
-o offset set initial directory offset
-r read/display raw image data instead of decoded data
-s display strip offsets and byte counts
-w display raw data in words rather than bytes
-z enable strip chopping
-# set initial directory (first directory is # 0)

code coverage

代码覆盖率是一个软件度量指标,用于显示每行代码被触发的次数。通过使用代码覆盖率,我们可以知道哪些部分的代码被模糊测试器(fuzzer)所覆盖,并可视化模糊测试的过程。

要做到代码覆盖率统计,需要安装 lcov

1
sudo apt install lcov
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 用 --coverage 重新编译 libTIFF
rm -r $HOME/fuzzing_tiff/install
cd $HOME/fuzzing_tiff/tiff-4.0.4/
make clean

CFLAGS="--coverage" LDFLAGS="--coverage" ./configure --prefix="$HOME/fuzzing_tiff/install/" --disable-shared
make
make install

# 通过下面的命令来获取代码覆盖率
cd $HOME/fuzzing_tiff/tiff-4.0.4/
# Reset previous counters
lcov --zerocounters --directory ./
# Return the "baseline" coverage data file that contains zero coverage for every instrumented line
lcov --capture --initial --directory ./ --output-file app.info
# Run the application you want to analyze . You can run it multiple times with different inputs
$HOME/fuzzing_tiff/install/bin/tiffinfo -D -j -c -r -s -w $HOME/fuzzing_tiff/tiff-4.0.4/test/images/palette-1c-1b.tiff
# Save the current coverage state into the app2.info file
lcov --no-checksum --directory ./ --capture --output-file app2.info

生成 html 输出文件

1
genhtml --highlight --legend -output-directory ./html-coverage/ ./app2.info

在当前目录存在一个 html-coverage 文件夹,打开 ./html-coverage/index.html 文件就可以看到,具体的代码覆盖率报告

nipaste_2024-07-07_17-04-3

随便点进去一个文件就可以查看它的覆盖率信息

分析

现在开始 fuzz libTIFF

1
2
3
4
5
6
7
8
9
10
# 使用 afl 对 libtiff 进行编译插桩
rm -r $HOME/fuzzing_tiff/install
cd $HOME/fuzzing_tiff/tiff-4.0.4/
make clean

# 这里依旧使用 AFL_USE_ASAN 用来内存检测
export LLVM_CONFIG="llvm-config-11"
CC=afl-clang-lto ./configure --prefix="$HOME/fuzzing_tiff/install/" --disable-shared
AFL_USE_ASAN=1 make -j4
AFL_USE_ASAN=1 make install

执行下面的 fuzz 命令即可

afl-fuzz -m none -i $HOME/fuzzing_tiff/tiff-4.0.4/test/images/ -o $HOME/fuzzing_tiff/out/ -s 123 -- $HOME/fuzzing_tiff/install/bin/tiffinfo -D -j -c -r -s -w @@

nipaste_2024-07-07_17-48-1

随便找一个就可以看见 ASAN 的分析报告

nipaste_2024-07-08_10-12-4

  • 蓝色部分为读取操作,大概意思就是在线程为T0中,在0x6020000000b1处读取了大小为2的数据,并显示了函数栈的信息。
  • 绿色部分为溢出的情况,其中还显示了堆块分配的函数栈

CVE-2016-9297 补丁

nipaste_2024-07-08_11-02-5

当最后一个字节不为 0 时,程序会强制将其赋值为 0 字节,因而避免越界读的问题。

尝试生成覆盖率报告

1
2
3
4
5
6
7
8
9
10
11
12
# 注意这里如果用的是afl编译的libTIFF的话,我们需要用 --coverage 重新编译 libTIFF
rm -r $HOME/fuzzing_tiff/install
cd $HOME/fuzzing_tiff/tiff-4.0.4/
make clean
CFLAGS="--coverage" LDFLAGS="--coverage" ./configure --prefix="$HOME/fuzzing_tiff/install/" --disable-shared
make
make install

$HOME/fuzzing_tiff/install/bin/tiffinfo -D -j -c -r -s -w $HOME/fuzzing_tiff/out/default/crashes/id:000007,sig:06,src:000303,time:167126,execs:68988,op:havoc,rep:2

lcov --no-checksum --directory ./ --capture --output-file app2.info
genhtml --highlight --legend -output-directory ./html-coverage2/ ./app2.info

nipaste_2024-07-08_10-39-1

总结

但是对于整个程序具体的功能及如何触发越界读流程,还需要我们对程序足够了解和深入理解

Exercise 5:LibXML2

libxml2是一个功能强大的C语言库,主要用于解析和处理XML文档。

CVE-2017-9048 is an stack buffer overflow vulnerability affecting the DTD validation functionality of LibXML2.

As a result, a remote attacker can exploit this issue to execute arbitrary code within the context of an application using the affected library.

You can find more information about stack buffer oveflow vulnerabilities at the following link: https://cwe.mitre.org/data/definitions/121.html

知识点

  • 自定义字典帮助找到新的执行路径
  • 使用多核来并行执行 fuzz 任务

Dictionaries

当需要 fuzz 复杂文本格式的文档时(如 xml),用一个包含有基本语法 token 的字典对于fuzz的效果来说是非常有用的。

在AFL(American Fuzzy Lop,一个模糊测试工具)的上下文中,这样的字典只是一组单词或值,被AFL用于对当前内存中的文件应用更改。具体来说,AFL使用字典中提供的值进行以下更改:

  • 覆盖(Override):用字典条目中的n个字节替换文件中的特定位置,其中n是字典条目的长度。
  • 插入(Insert):在当前文件位置插入字典条目,迫使所有字符向下移动n个位置,并增加文件大小。

可以在这里 找到一些字典样本文件

Paralellization

最大化并行利用多核 cpu 资源可以显著提高 fuzz 的效率。

Independent instances

在独立实例下,我们需要为每一个运行的 AFL 实例创建一个独立的输出文件夹,同时如果使用 -s 标志,还需要为每一个 AFL 实例设置不同的 seed。

Shared instances

在共享实例下,每个 fuzz 实例汇合了其他实例的测试集,同时在一个时间内,只能有一个 main instance

1
./afl-fuzz -i afl_in -o afl_out -M Master -- ./program @@

和其他 N-1个 slaves:

1
2
3
4
./afl-fuzz -i afl_in -o afl_out -S Slave1 -- ./program @@
./afl-fuzz -i afl_in -o afl_out -S Slave2 -- ./program @@
...
./afl-fuzz -i afl_in -o afl_out -S SlaveN -- ./program @@

环境搭建

1
2
3
4
5
6
7
8
9
10
11
12
cd $HOME
mkdir Fuzzing_libxml2 && cd Fuzzing_libxml2

# Download and uncompress libxml2-2.9.4.tar.gz
wget http://xmlsoft.org/download/libxml2-2.9.4.tar.gz
tar xvf libxml2-2.9.4.tar.gz && cd libxml2-2.9.4/

# Build and install libxml2:
sudo apt-get install python-dev
CC=afl-cc CXX=afl-c++ CFLAGS="-fsanitize=address" CXXFLAGS="-fsanitize=address" LDFLAGS="-fsanitize=address" ./configure --prefix="$HOME/Fuzzing_libxml2/libxml2-2.9.4/install" --disable-shared --without-debug --without-ftp --without-http --without-legacy --without-python LIBS='-ldl'
make -j$(nproc)
make install

测试如下

./xmllint --memory ./test/wml.xml

nipaste_2024-07-08_14-08-0

创建语料库

1
2
3
mkdir afl_in && cd afl_in
wget https://raw.githubusercontent.com/antonio-morales/Fuzzing101/main/Exercise%205/SampleInput.xml
cd ..

指定字典

1
2
3
mkdir dictionaries && cd dictionaries
wget https://raw.githubusercontent.com/AFLplusplus/AFLplusplus/stable/dictionaries/xml.dict
cd ..

开始fuzz

创建一个 main instance

1
afl-fuzz -m none -i ./afl_in -o afl_out -s 321 -x ./dictionaries/xml.dict -D -M master -- ./libxml2-2.9.4/xmllint --memory --noenc --nocdata --dtdattr --loaddtd --valid --xinclude @@

在另外一个 terminal 创建另外一个 slave instance

1
afl-fuzz -m none -i ./afl_in -o afl_out -s 234 -S slave1 -- ./libxml2-2.9.4/xmllint --memory --noenc --nocdata --dtdattr --loaddtd --valid --xinclude @@

然后我们就可以分别看到如下图所示的两个 fuzz 界面,一个是 master instance,一个是 slave1 master

nipaste_2024-07-08_14-25-2

nipaste_2024-07-08_14-25-3

但是经过7天之后,最终结果如下:

nipaste_2024-07-15_16-00-4

我直呼精彩,但也无所谓,这些 Exercise 也仅仅是用来帮助我们掌握使用 AFL 进行 fuzz 的过程。

Reference

https://blog.xmcve.com/2023/01/13/Fuzzing101%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/

  • Title: fuzz101 Exercise
  • Author: henry
  • Created at : 2024-07-15 16:04:49
  • Updated at : 2024-07-15 16:07:13
  • Link: https://henrymartin262.github.io/2024/07/15/fuzzing101/
  • License: This work is licensed under CC BY-NC-SA 4.0.
 Comments