0x01 漏洞信息
1.漏洞简述
• 漏洞名称:Libtiff 越界读取漏洞
• 漏洞编号:CVE-2016-9297
• 漏洞类型:越界读取
• 漏洞影响:拒绝服务
• CVSS评分:(CVSS 3.0 )7.5
• 利用难度:低
• 基础权限:不需要
2.组件概述
LibTIFF是一个开源的用于处理TIFF(Tagged Image File Format)图像文件的C/C++库。TIFF是一种常见的图像文件格式,广泛应用于图像处理和存储。LibTIFF提供了一组功能丰富的API,允许开发者读取、写入、编辑和处理TIFF格式的图像。
3.漏洞利用
使用tiffinfo解析Fuzzing出的样本。
4.漏洞影响
Libtiff <= 4.0.6
5.解决方案
更新至Libtiff4.0.7以上版本
0x02 漏洞复现
1. 环境准备
目录创建
cd $HOME
mkdir fuzzing_tiff && cd fuzzing_tiff/
下载tiff-4.0.4
wget https://download.osgeo.org/libtiff/tiff-4.0.4.tar.gz
tar -xzvf tiff-4.0.4.tar.gz
使用ASAN模糊测试。
rm -r $HOME/fuzzing_tiff/install
cd $HOME/fuzzing_tiff/tiff-4.0.4/
make clean
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
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 @@
2.FUZZING
0x03 漏洞分析
1. 基本信息
漏洞产生在tif_dirread.c函数中,在Field的TIFFSetGetFieldType属性为TIFF_SETGET_C32_ASCII或TIFF_SETGET_C16_ASCII时,输出Tag Value未在字符串末尾置0,从而可能导致越界读取。
2. 详细分析
2.1基础分析
此次的分析基于Fuzzing101的Exercise4所以选择4.0.4版本进行分析,CVE对该漏洞的描述为:
The TIFFFetchNormalTag function in LibTiff 4.0.6 allows remote attackers to cause a denial of service (out-of-bounds read) via crafted TIFF_SETGET_C16ASCII or TIFF_SETGET_C32_ASCII tag values.
在LibTiff 4.0.6中,TIFFFetchNormalTag函数存在漏洞,该漏洞可以被远程攻击者利用,通过精心构造的TIFF_SETGET_C16ASCII或TIFF_SETGET_C32_ASCII标签值来造成拒绝服务(越界读取)攻击。
2.2静态分析
查看一下crash文件。
00h~01h:0x4949,使用小端序
02h~03h:0x002a,十进制42,为tif文件标识符。
04h~07h:第一个IFD(Image File Dirctory)的偏移。
随后是10h开始的第一个IFD结构。
10h~11h:Directory Entries的数目。此处为11。
后续每12字节代表1个IFD Entry。
最后一个IFD Entry的后四个字节表示下一个IFD结构的偏移,如果没有为0。
IFD Entry结构(一个Entry也可以叫做一个field)。
00h~01h:该field的标识。
02h~03h :该field的数据类型。
04h~07h:数量,Count of the indicated Type,根据数量和类型确定其字节数。
08h~0Bh:值的文件偏移,如果该tag value占用字节数小于四字节,那么value存放于此。
划分一下数据
红框处是下面出现crash寄存器出现的Tag 63253。此处可以注意一下。
tag:0xf715 63253
type:0x0002 ASCII类型
数量:0x00000001 1
Offset or 数据:0x00000003 3
2.3动态分析
AFL+ASAN对Libtiff4.0.4进行Fuzzing,执行Fuzzing出的Crash,得到提示,部分DirectoryEntry(简称DE结构)解析错误,错误为堆溢出。
根据官方提示在__asan::ReportGenericError下断点,这里中断在asan报告错误之前,发生错误之后的地方,bt查看调用栈,产生错误的代码在tif_print.c:127。
由于ASAN会对程序进行插桩,阻碍汇编代码的阅读,所以笔者重新编译了一下源程序方便后续的调试。
gdb --args ./install/bin/tiffinfo -D -j -c -r -s -w ./out/default/crashes/crash对程序进行调试,从上述信息得出断点位置b tif_print.c:126,执行的时候上下文信息如图。
这里将raw_data字符串打印了出来,raw_data对应的field为Tag 63253对应的field也就是我们crash文件红框标识的0xf715那一个DE结构。可以猜想一下逻辑,该DE结构被读入内存,输出的时候分配了一段内存空间用来存储其Value,随后根据其DE结构的类型(ASCII)输出Tag和对应的Value。回想一下,当时我们的数据长度为0x1,每一个ASCII类型占8位,所以我们该Tag的Value应该是0x3,但是这里显然,如果将raw_data输出,会输出03 3b e4 e4 f7 ff 7f。这里显然访问了不应该访问的内存。但是这并不会产生crash,因为该内存地址是可读写的。
随后raw_data的内存下一个写入断点,重新执行程序,看看它是什么时候写入的。
第一次,该内存存放了一个_TIFFField结构指针
struct _TIFFField {
uint32 field_tag; /* field's tag */
short field_readcount; /* read count/TIFF_VARIABLE/TIFF_SPP */
short field_writecount; /* write count/TIFF_VARIABLE */
TIFFDataType field_type; /* type of associated data */
uint32 reserved; /* reserved for future extension */
TIFFSetGetFieldType set_field_type; /* type to be passed to TIFFSetField */
TIFFSetGetFieldType get_field_type; /* type to be passed to TIFFGetField */
unsigned short field_bit; /* bit in fieldsset bit vector */
unsigned char field_oktochange; /* if true, can change while writing */
unsigned char field_passcount; /* if true, pass dir count on set */
char* field_name; /* ASCII name */
TIFFFieldArray* field_subfields; /* if field points to child ifds, child ifd field definition array */
};
从信息可以看出,该结构是一些的Tag信息,由于Tag 63253是一个在标准的Tag列表中没有,所以会进行一次Merge。
第三次,0x00007ffff7e43be0和最后输出的值很接近了。
bt查看一下调用栈。笔者决定从tif_dirread.c:5351开始跟一遍。
TIFFSetField函数传入了一个DE结构,一个data,这个data存放的值Tag 的Value。
随后进入内部,发现该函数做了如下操作,执行_TIFFVSetField函数找到Tag对应的TIFField malloc一个TIFFValue给它,最后分配了一个空间存储Value,该空间的起始地址也就是后续print的raw_data,尽管只分配了1字节的空间,但是由于是64位的Linux,内存分配的大小最小也是0x20字节,所以如下图,0x20为chunkSize,03为存储的Value。
后续打印会打印0x7ffff7e43b03,属于是越界读取。但是由于该地方的内存只是单纯的一个地址信息,且由内存管理机制决定,所以似乎无法造成信息泄露。尝试修改Tag的Value数目使得读取其他内存,其计算的偏移会和文件的Size进行一个比较,无法利用。
2.4 补丁分析
在输出字符串的结尾提前放置了\x00防止越界读取。
但是显然这里产生了新的漏洞,如果dp->tdir_count=0,那么data就是NULL,这时候对空指针进行了Read操作。
CVE-2016-9448 的描述看起来起来就是这个原因。
按照第一次的补丁修改一下源码,随后修改Tag的Count为0,果然出现了Segmentation fault。
文章评论