fuzzing-2

学习网站,,这次主要基于libexif,fizz共享库。
封校了,安心学习ing

本次尝试使用afl++对一个library进行fuzz。fuzz本身倒是不难,我觉得难的是配置这样那样的环境,找到合适的训练样本。

按照练习的说法,我们的workflow是

  1. Find an interface application that makes use of the libexif library
  2. Create a seed corpus of exif samples
  3. Compile libexif and the chosen application to be fuzzed using afl-clang-lto
  4. Fuzz libexif until you have a few unique crashes
  5. Triage the crashes to find a PoC for each vulnerability
  6. Fix the issues

环境

环境配置

找到libexif倒不是很困难,难的是编译的时候总是报错。直接在github上面搜索libexif即可。链接。但是注意要下特定版本的(0.6.14)。这里遇到了第一个问题,怎么下载历史版本library。看了答案,完全不理解怎么找到的这个网站。甚至直接浏览器搜索https://github.com/libexif/libexif/archive/都是404,但是却可以下载。

1
wget https://github.com/libexif/libexif/archive/refs/tags/libexif-0_6_14-release.tar.gz

接下来解压

1
tar -xzvf libexif-0_6_14-release.tar.gz

接下来是编译。这里找到了一个很好的介绍编译选项的网站,然后发现其实在这里也有下载链接,不用去github那个诡异的链接里面下载了。

image-20220314102227098

通过这个网站的学习,了解到configure的用法——生成makefile文件。具体的我不搬运了,讲的挺好的。重点写一下自己学到的。

一般git clone下载的文件,由于不知道要在那些系统上(windows,linux等),用什么编译器(gcc,clang)编译,因此需要生成一个符合系统配置的makefile文件。这时就需要用configure生成了。除此以外,手工编写makefile文件也是很大的挑战。具体来说,有以下几步

利用命令

1
autoreconf -fvi

来生成一个全新的configure文件。参考

之后设置configure的参数。我们重点设置安装路径和使用的编译器种类。(编译器可能不设置默认为gcc?)这里是看了答案的。类似于上一次lab,指定安装路径为/install文件夹即可。

1
./configure --enable-shared=no --prefix="$HOME/fuzzing_libexif/install/"

之后就可以make了。

1
make && sudo make install

期间遇到了一些问题,记录在下文了。

之后嘞,需要找一个使用libexif的文件,最好不是GUI的。我们在libexif的官网上其实能找到。

image-20220314111515233

看到答案是下了exif,我这里也下这个吧。之后编译exif的过程和上面差不多,网上也能查到,看答案也没问题。需要注意的是这里的configure文件编写。

1
./configure --enable-shared=no --prefix="$HOME/fuzzing_libexif/install/" PKG_CONFIG_PATH=$HOME/fuzzing_libexif/install/lib/pkgconfig

这里的PKG_CONFIG_PATH是什么意思呢?网上查

PKG_CONFIG_PATH是一个环境变量,它指定pkg-config将在其中搜索其.pc文件的其他路径。

此变量用于增强pkg-config的默认搜索路径。在典型的Unix系统上,它将搜索目录/usr/lib/pkgconfig/usr/share/pkgconfig。这通常包括系统安装的模块。但是,某些本地模块可能安装在不同的前缀中,例如/usr/local。在这种情况下,必须预先设置搜索路径,以便pkg-config可以找到.pc文件。

我的理解是,既然我们把libexif它安装在了我们指定路径下(编译libexif时的configure),这里就需要指定pkgconfig,因为它并不是安装在上面写的默认搜索路径中。在这两步之后,就能够正常运行libexif了。

环境配置遇到的问题

尝试./configure时:

1
2
3
4
5
6
7
* Fatal: libexif command line interface requires libexif to build.
*
* Possible solutions:
* - set PKG_CONFIG_PATH to adequate value
* - call configure with LIBEXIF_LIBS=.. and LIBEXIF_CFLAGS=..
* - call configure with one of the --with-libexif parameters
* - get libexif and install it:

解决:这个是exif而不是libexif。需要先安装libexif。

尝试make install

1
make[2]: *** No rule to make target 'install-apidocs', needed by 'install-data-local'.  Stop.

解决:这个似乎完全不需要管,程序照样能运行。(输入/home/fuzz/fuzzing_libexif/install/bin/exif)可以看到输出。

image-20220314112758534

fuzz

在fuzz之前,按照练习的说法,需要用afl-clang-lto重新编译上述两个文件。只需要指定CC与CXX两个环境变量即可。(这里为什么是llvm-config-11不太清楚,不过看说明这应该是一个专门写在config上面的配置文件)

image-20220314130801214

以下是代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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-lto实际上是首选项。这样会导致之前讲到的hashmap冲突发生的可能性更小。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
+--------------------------------+
| clang/clang++ 11+ is available | --> use LTO mode (afl-clang-lto/afl-clang-lto++)
+--------------------------------+ see [instrumentation/README.lto.md](instrumentation/README.lto.md)
|
| if not, or if the target fails with LTO afl-clang-lto/++
|
v
+---------------------------------+
| clang/clang++ 3.8+ is available | --> use LLVM mode (afl-clang-fast/afl-clang-fast++)
+---------------------------------+ see [instrumentation/README.llvm.md](instrumentation/README.llvm.md)
|
| if not, or if the target fails with LLVM afl-clang-fast/++
|
v
+--------------------------------+
| gcc 5+ is available | -> use GCC_PLUGIN mode (afl-gcc-fast/afl-g++-fast)
+--------------------------------+ see [instrumentation/README.gcc_plugin.md](instrumentation/README.gcc_plugin.md) and
[instrumentation/README.instrument_list.md](instrumentation/README.instrument_list.md)
|
| if not, or if you do not have a gcc with plugin support
|
v
use GCC mode (afl-gcc/afl-g++) (or afl-clang/afl-clang++ for clang)

使用以下语句完成编译

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

解释一下上面语句的含义。-i指定了输入文件,-o指定输出文件夹(这两个都是fuzzer所需要的),-s是seed,一个随机种子。后面的--代表命令行的输入,也就是我们如何从命令行调用这个文件,@@表示afl将在这里替换我们的输入,也就是上面-i的内容。跑了一会儿之后得到下面的内容。

image-20220314105819541

发现还是出现了蛮多错误的,都是sigmentation fault。

image-20220314131953777

image-20220314132049731

调试

之前是用gdb调试,这次练习教的是用eclipse。但是我不想用,linux中图形化支持有点慢。

1
2
3
4
Program received signal SIGSEGV, Segmentation fault.
0x000000000022baef in exif_data_load_data (data=0x4535b0, d_orig=<optimized out>, ds_orig=<optimized out>) at exif-utils.c:92
92 return ((buf[0] << 8) | buf[1]);

image-20220314132420032

还是用的gdb。可以看到停在了这里

image-20220314132857871

这里$rcx并不是一个合理的值,可能是溢出之后的结果。就是这里导致了segmentation fault。

image-20220314132952712

源码查看

使用以下命令递归查找文件

1
2
find . -name "exif-utils.c"
# ./libexif/exif-utils.c

之后利用先前的gdb中backtrace(bt)查找调用栈。这里学到的一点是不能再fault之后再backtrace,好像和断在之前不一样。以下是断在segmentation fault之前的结果。

image-20220314141910400

漏洞函数为

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]); <== crash here
case EXIF_BYTE_ORDER_INTEL:
return ((buf[1] << 8) | buf[0]);
}

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

由上面的分析,是传入的第一个参数有问题。

向上追溯,定位到以下关键函数

1
2
3
4
5
ExifShort
exif_get_short (const unsigned char *buf, ExifByteOrder order)
{
return (exif_get_sshort (buf, order) & 0xffff);
}

接下来向上寻找,到exif_data_load_data第804行和819行

1
2
3
4
5
6
7
8
	/* Fixed value */
if (exif_get_short (d + 8, data->priv->order) != 0x002a)
return;
// line 819
n = exif_get_short (d + 6 + offset, data->priv->order);
if (offset + 6 + 2 + 12 * n + 4 > ds) {
return;
}

可以看到这里给d也就是buf加了一个8。可能就是这里的问题吗?(假设d为空?)断在这个函数之前调试一下。

image-20220314143045368

看源码发现memcmp用的参数1和exif_get_short用的参数是一样的,查看了一下寄存器的值

image-20220314143628351

memcpy结束后,将会判定两者相等,从而跳出循环到下面的漏洞函数中。注意观察这一段汇编代码,正是判断是否等于0x002a的一段。

image-20220314143918338

这一段应该直接被优化成内联汇编展开了,而不是函数调用(卡了好久,,就说为什么没有断上去)但是这一段现在没有问题,直接经过了??

image-20220314145152643

找这个问题还是挺麻烦的。但是我们知道了汇编代码的行数,尝试直接在汇编代码上面下断点?

image-20220314145306512

终于找到了,原来有两个地方调用了exif_get_short,在819行还有一个。这里看的就比较清楚了,是offset越界导致的错误。注意4294967295是0xFFFFFFFF!好的,找到了一个整数溢出的bug。

image-20220314150233892

接下来就可以看源码找offset在哪里了。和offset相关的代码一定在这句话附近。我们逐个调试一下。分别在808,813行,最终定位到808行会出现问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
/* IFD 0 offset */
offset = exif_get_long (d + 10, data->priv->order);
exif_log (data->priv->log, EXIF_LOG_CODE_DEBUG, "ExifData",
"IFD 0 at %i.", (int) offset);

/* Parse the actual exif data (usually offset 14 from start) */
exif_data_load_data_content (data, EXIF_IFD_0, d + 6, ds - 6, offset, 0);

/* IFD 1 offset */
if (offset + 6 + 2 > ds) {
return;
}
n = exif_get_short (d + 6 + offset, data->priv->order);

image-20220314150952796

那搞了半天,应该是exif_get_long返回值的问题,如下

image-20220314151059213

下面是汇编部分,只用了一句汇编指令就完成了(不愧是intel!)关键是这里

image-20220314152919806

查询intel汇编手册

Description
Reverses the byte order of a 32-bit (destination) register: bits 0 through 7 are swapped with bits 24 through 31, and bits 8 through 15 are swapped with bits 16 through 23. This instruction is provided for converting little-endian values to big-endian format and vice versa.

确实一句话完成了这个工作。

1
2
3
(gdb) si
(gdb) p $ebp
$10 = -1

感觉已经接近真相了!看看exp是哪里来的,是0xa(%r15),应该就是这个函数的参数d+10!(exif-data.c第808行)

1
offset = exif_get_long (d + 10, data->priv->order);

这里应该就是一个越界读的操作了,通过以下命令可以看出。至此,完成了我们的分析。

image-20220314153340392

问题解决

对上面offset返回值加上一个sanity-check,控制大小范围。

总结

这次是fuzz共享库,一方面要找共享库文件、使用共享库的文件、输入集合;另外一方面要学会怎么编译一个共享库,怎么使用afl-clang-lto编译。最重要的是,调试并复现漏洞(真的花了将近六个小时),学到了一些,还是很有收获的。

  • 常用的下载网站

http://blog.fpliu.com/it/software/development/language/c/library/libexif/build-for-current-host

文章目录
  1. 1. 环境
    1. 1.1. 环境配置
    2. 1.2. 环境配置遇到的问题
  2. 2. fuzz
  3. 3. 调试
    1. 3.1. 源码查看
    2. 3.2. 问题解决
  4. 4. 总结
|