0%

Fuzz Lab4-tiff-4.0.4

Fuzz-Lab4:LibTIFF

这次我们将模糊 LibTIFF 图像库(读取和写入 tiff 文件最主要的一个开源库),目标是在 libtiff 4.0.4 中为 [CVE-2016-9297] 找到 crash/PoC,并 测量 crash/PoC 的代码覆盖率数据

  • CVE-2016-9297 是一个越界读取漏洞,可以通过特制的 TIFF_SETGET_C16_ASCII 或 TIFF_SETGET_C32_ASCII 标记值触发
    • 越界读取是当程序读取超出预期缓冲区末尾或开头之前的数据时发生的漏洞
    • 结果,它允许远程攻击者导致拒绝服务或可能从进程内存中获取潜在的敏感信息

完成本练习后,您将了解:

  • 如何使用 LCOV 测量代码覆盖率
  • 如何使用代码覆盖率数据来提高模糊测试的有效性

Do it yourself!

为了完成这个练习,你需要:

  • Fuzz LibTiff(启用 ASan)直到出现一些独特的崩溃
  • 对崩溃进行分类以找到漏洞的 PoC
  • 测量这个 PoC 的代码覆盖率
  • 修复问题

Download and build your target

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

cd ..
wget https://download.osgeo.org/libtiff/tiff-4.0.4.tar.gz # 下载libtiff-4.0.4
tar -xzvf tiff-4.0.4.tar.gz

cd tiff-4.0.4/ # 构建和安装libtiff
./configure --prefix="$HOME/fuzzing_tiff/install2/" --disable-shared
make
make install

作为目标二进制文件,我们可以对位于 /bin 文件夹中的 tiffinfo 二进制文件进行模糊测试,作为种子输入语料库,我们将使用 /test/images/ 文件夹中的示例图像

要测试一切是否正常,只需键入:

1
$HOME/fuzzing_tiff/install2/bin/tiffinfo -D -j -c -r -s -w $HOME/tiff-4.0.4/test/images/palette-1c-1b.tiff
  • 在最后一个命令行中,您可以看到我启用了所有这些标志:“-j -c -r -s -w”,这是为了提高 代码覆盖率 并增加发现错误的机会

结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
TIFF Directory at offset 0xbd4 (3028)
Image Width: 157 Image Length: 151
Bits/Sample: 1
Sample Format: unsigned integer
Compression Scheme: None
Photometric Interpretation: palette color (RGB from colormap)
Samples/Pixel: 1
Rows/Strip: 409
Planar Configuration: single image plane
Page Number: 0-1
Color Map:
0: 0 0 0
1: 65535 65535 65535
DocumentName: palette-1c-1b.tiff
Software: GraphicsMagick 1.2 unreleased Q16 http://www.GraphicsMagick.org/
1 Strips:
0: [ 8, 3020]
  • 标签图像文件格式(Tag Image File Format,TIFF)是一种灵活的位图格式,主要用来存储包括照片和艺术图在内的图像
  • 而 LibTIFF 就是打开 TIFF 文件的一种工具

Code coverage

代码覆盖率是一种软件指标,显示每行代码被触发的次数,通过使用代码覆盖率,我们将了解模糊器已到达代码的哪些部分并 可视化 模糊测试过程

首先,我们需要安装 lcov ,我们可以使用以下命令来完成:

1
sudo apt install lcov
  • lcov 是 gcc 测试覆盖率的前端图形展示工具

现在我们需要使用 —coverage 标志(编译器和链接器)重建 libTIFF:

1
2
3
4
5
6
cd $HOME/tiff-4.0.4/
make clean

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

然后我们依次输入以下内容来收集代码覆盖率数据:

1
2
3
4
5
cd $HOME/tiff-4.0.4/
lcov --zerocounters --directory ./ # 重置以前的计数器
lcov --capture --initial --directory ./ --output-file app.info # 返回“基线”覆盖率数据文件,其中包含每条检测线的零覆盖率
$HOME/fuzzing_tiff/install/bin/tiffinfo -D -j -c -r -s -w $HOME/tiff-4.0.4/test/images/palette-1c-1b.tiff # 运行您要分析的应用程序,您可以使用不同的输入多次运行它
lcov --no-checksum --directory ./ --capture --output-file app2.info # 将当前覆盖状态保存到app2.info文件中

最后,我们必须生成 HTML 输出:

1
genhtml --highlight --legend -output-directory ./html-coverage/ ./app2.info
  • 如果一切顺利,代码覆盖率报告将在 html-coverage 文件夹中创建,只需打开 ./html-coverage/index.html 文件,您应该会看到如下内容:
  • 其实我并不知道这个东西有什么用

Fuzz LibTIFF

现在我们将在启用 ASAN 的情况下编译 libtiff

首先,我们要清理所有以前编译的目标文件和可执行文件:

1
2
3
rm -r $HOME/fuzzing_tiff/install
cd $HOME/tiff-4.0.4/
make clean

现在,我们在调用 make 之前设置 AFL_USE_ASAN=1:

1
2
3
4
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

现在,您可以使用以下命令运行模糊器:

1
afl-fuzz -m none -i $HOME/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 @@

结果:

Triage

接下来我们要使用 ASan 对崩溃进行分类

调试使用 ASan 构建的程序比前面的练习要容易得多,我们需要做的就是向程序提供崩溃文件:

1
$HOME/fuzzing_tiff/install/bin/tiffinfo -D -j -c -r -s -w ./id:000000,sig:11,src:000009,time:67581,execs:76549,op:havoc,rep:4 

结果如下:

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
TIFFReadDirectory: Warning, Bogus "StripByteCounts" field, ignoring and calculating from imagelength.
TIFF Directory at offset 0xbd4 (3028)
Image Width: 157 Image Length: 151
Bits/Sample: 1537
Sample Format: unsigned integer
Compression Scheme: None
Photometric Interpretation: palette color (RGB from colormap)
Samples/Pixel: 1
Rows/Strip: 409
Planar Configuration: single image plane
Page Number: 0-1
Color Map:
AddressSanitizer:DEADLYSIGNAL
=================================================================
==3472169==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000 (pc 0x00000046e5f3 bp 0x7ffdb4e2bf10 sp 0x7ffdb4e2bd80 T0)
==3472169==The signal is caused by a READ memory access.
==3472169==Hint: address points to the zero page.
#0 0x46e5f3 in TIFFPrintDirectory /home/yhellow/tiff-4.0.4/libtiff/tif_print.c
#1 0x33fbad in tiffinfo /home/yhellow/tiff-4.0.4/tools/tiffinfo.c:449:2
#2 0x33f1d4 in main /home/yhellow/tiff-4.0.4/tools/tiffinfo.c:152:6
#3 0x7f60e75b6082 in __libc_start_main /build/glibc-SzIz7B/glibc-2.31/csu/../csu/libc-start.c:308:16
#4 0x29165d in _start (/home/yhellow/fuzzing_tiff/install/bin/tiffinfo+0x29165d)

AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV /home/yhellow/tiff-4.0.4/libtiff/tif_print.c in TIFFPrintDirectory
==3472169==ABORTING
  • 输出的上半截就是报错信息,下半截是执行跟踪
  • 程序 crash 的原因是无效的地址 0x000000000000

除了这种 crash 以外,还有其他原因:

1
$HOME/fuzzing_tiff/install/bin/tiffinfo -D -j -c -r -s -w ./id:000002,sig:06,src:000009,time:81829,execs:92343,op:havoc,rep:8 
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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
TIFFReadDirectoryCheckOrder: Warning, Invalid TIFF directory; tags are not sorted in ascending order.
TIFFReadDirectory: Warning, Unknown field with tag 7217 (0x1c31) encountered.
TIFF Directory at offset 0xbd4 (3028)
Image Width: 157 Image Length: 151
Bits/Sample: 1
Sample Format: unsigned integer
Compression Scheme: None
Photometric Interpretation: palette color (RGB from colormap)
Samples/Pixel: 1
Rows/Strip: 409
Planar Configuration: single image plane
Page Number: 0-1
Color Map:
0: 29281 26979 24935
1: 28776 29517 26979
DocumentName: palette-1c-1b.tiff
=================================================================
==3582061==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x6070000000d1 at pc 0x0000002aadf2 bp 0x7ffc31f49e70 sp 0x7ffc31f49630
READ of size 66 at 0x6070000000d1 thread T0
#0 0x2aadf1 in fputs (/home/yhellow/fuzzing_tiff/install/bin/tiffinfo+0x2aadf1)
#1 0x47068f in _TIFFPrintField /home/yhellow/tiff-4.0.4/libtiff/tif_print.c:127:4
#2 0x47068f in TIFFPrintDirectory /home/yhellow/tiff-4.0.4/libtiff/tif_print.c:641:5
#3 0x33fbad in tiffinfo /home/yhellow/tiff-4.0.4/tools/tiffinfo.c:449:2
#4 0x33f1d4 in main /home/yhellow/tiff-4.0.4/tools/tiffinfo.c:152:6
#5 0x7f68b28f1082 in __libc_start_main /build/glibc-SzIz7B/glibc-2.31/csu/../csu/libc-start.c:308:16
#6 0x29165d in _start (/home/yhellow/fuzzing_tiff/install/bin/tiffinfo+0x29165d)

0x6070000000d1 is located 0 bytes to the right of 65-byte region [0x607000000090,0x6070000000d1)
allocated by thread T0 here:
#0 0x30b6cd in malloc (/home/yhellow/fuzzing_tiff/install/bin/tiffinfo+0x30b6cd)
#1 0x3564d5 in _TIFFmalloc /home/yhellow/tiff-4.0.4/libtiff/tif_unix.c:283:10
#2 0x3564d5 in setByteArray /home/yhellow/tiff-4.0.4/libtiff/tif_dir.c:51:19
#3 0x3564d5 in _TIFFVSetField /home/yhellow/tiff-4.0.4/libtiff/tif_dir.c:539:4
#4 0x34c2d6 in TIFFVSetField /home/yhellow/tiff-4.0.4/libtiff/tif_dir.c:820:6
#5 0x34c2d6 in TIFFSetField /home/yhellow/tiff-4.0.4/libtiff/tif_dir.c:764:11
#6 0x38464e in TIFFFetchNormalTag /home/yhellow/tiff-4.0.4/libtiff/tif_dirread.c:5164:8
#7 0x377976 in TIFFReadDirectory /home/yhellow/tiff-4.0.4/libtiff/tif_dirread.c:3810:12
#8 0x433bc9 in TIFFClientOpen /home/yhellow/tiff-4.0.4/libtiff/tif_open.c:466:8
#9 0x33ef47 in TIFFFdOpen /home/yhellow/tiff-4.0.4/libtiff/tif_unix.c:178:8
#10 0x33ef47 in TIFFOpen /home/yhellow/tiff-4.0.4/libtiff/tif_unix.c:217:8
#11 0x33ef47 in main /home/yhellow/tiff-4.0.4/tools/tiffinfo.c:140:9
#12 0x7f68b28f1082 in __libc_start_main /build/glibc-SzIz7B/glibc-2.31/csu/../csu/libc-start.c:308:16

SUMMARY: AddressSanitizer: heap-buffer-overflow (/home/yhellow/fuzzing_tiff/install/bin/tiffinfo+0x2aadf1) in fputs
Shadow bytes around the buggy address:
0x0c0e7fff7fc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c0e7fff7fd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c0e7fff7fe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c0e7fff7ff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c0e7fff8000: fa fa fa fa fd fd fd fd fd fd fd fd fd fa fa fa
=>0x0c0e7fff8010: fa fa 00 00 00 00 00 00 00 00[01]fa fa fa fa fa
0x0c0e7fff8020: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c0e7fff8030: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c0e7fff8040: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c0e7fff8050: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c0e7fff8060: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
Addressable: 00
Partially addressable: 01 02 03 04 05 06 07
Heap left redzone: fa
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
Container overflow: fc
Array cookie: ac
Intra object redzone: bb
ASan internal: fe
Left alloca redzone: ca
Right alloca redzone: cb
Shadow gap: cc
==3582061==ABORTING
  • 这种 crash 的原因就是堆溢出
  • 目前只发现这两种类型的 crash

经过对比发现:[CVE-2016-9297] 的 PoC 为 id:000002(通过精心编制的 TIFF_SETGET_C16ASCII 或 TIFF_SETGET_C32_ASCII 标记值来造成越界读取)

PoC Code coverage

1
2
3
4
5
6
cd $HOME/tiff-4.0.4/
make clean

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

然后我们依次输入以下内容来收集代码覆盖率数据:

1
2
3
4
5
cd $HOME/tiff-4.0.4/
lcov --zerocounters --directory ./ # 重置以前的计数器
lcov --capture --initial --directory ./ --output-file app.info # 返回“基线”覆盖率数据文件,其中包含每条检测线的零覆盖率
$HOME/fuzzing_tiff/coverage/bin/tiffinfo -D -j -c -r -s -w $HOME/fuzzing_tiff/out/default/crashes/test # 运行您要分析的应用程序,您可以使用不同的输入多次运行它
lcov --no-checksum --directory ./ --capture --output-file app2.info # 将当前覆盖状态保存到app2.info文件中

最后,我们必须生成 HTML 输出:

1
genhtml --highlight --legend -output-directory ./html-coverage/ ./app2.info
  • 如果一切顺利,代码覆盖率报告将在 html-coverage 文件夹中创建,只需打开 ./html-coverage/index.html 文件,您应该会看到如下内容:

Reproduce the crash

先使用没有 ASan 的 tiffinfo 来运行 crash 文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
➜  bin ./tiffinfo test
TIFFReadDirectoryCheckOrder: Warning, Invalid TIFF directory; tags are not sorted in ascending order.
TIFFReadDirectory: Warning, Unknown field with tag 7217 (0x1c31) encountered.
TIFF Directory at offset 0xbd4 (3028)
Image Width: 157 Image Length: 151
Bits/Sample: 1
Sample Format: unsigned integer
Compression Scheme: None
Photometric Interpretation: palette color (RGB from colormap)
Samples/Pixel: 1
Rows/Strip: 409
Planar Configuration: single image plane
Page Number: 0-1
Color Map: (present)
DocumentName: palette-1c-1b.tiff
Tag 7217: GraphicsMagick 1.2 unreleased Q16 http://ww��������������������w.#)
  • 发现程序的输出异常,我们先利用 ASan 进行调试,ASan 提供的报错信息如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
==3333==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x6070000000d1 at pc 0x0000002aadf2 bp 0x7fffffffda30 sp 0x7fffffffd1f0
READ of size 66 at 0x6070000000d1 thread T0
[Attaching after Thread 0x7ffff7c01800 (LWP 3333) fork to child process 3337]
[New inferior 2 (process 3337)]
[Detaching after fork from parent process 3333]
[Inferior 1 (process 3333) detached]
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
process 3337 is executing new program: /usr/lib/llvm-11/bin/llvm-symbolizer
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
#0 0x2aadf1 in fputs (/home/yhellow/fuzzing_tiff/install/bin/tiffinfo+0x2aadf1)
#1 0x47068f in _TIFFPrintField /home/yhellow/tiff-4.0.4/libtiff/tif_print.c:127:4
#2 0x47068f in TIFFPrintDirectory /home/yhellow/tiff-4.0.4/libtiff/tif_print.c:641:5
#3 0x33fbad in tiffinfo /home/yhellow/tiff-4.0.4/tools/tiffinfo.c:449:2
#4 0x33f1d4 in main /home/yhellow/tiff-4.0.4/tools/tiffinfo.c:152:6
#5 0x7ffff7c28082 in __libc_start_main /build/glibc-SzIz7B/glibc-2.31/csu/../csu/libc-start.c:308:16
#6 0x29165d in _start (/home/yhellow/fuzzing_tiff/install/bin/tiffinfo+0x29165d)
  • 根据 ASan 提供的信息,在 0x2aadf2 打上断点,对比溢出点执行前后 heap 的变化:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
*RAX  0x2aadf2 (fputs+386) ◂— lea    rdx, [rbp - 0x840]
*RBX 0x607000000090 ◂— 0x7363696870617247 ('Graphics')
*RCX 0x0
*RDX 0x14
*RDI 0x232900 (__asan::kInterceptorViaLibrary) ◂— 'interceptor_via_lib'
*RSI 0x232900 (__asan::kInterceptorViaLibrary) ◂— 'interceptor_via_lib'
*RBP 0x7fffffffda30 —▸ 0x7fffffffdbd0 —▸ 0x7fffffffdc90 —▸ 0x7fffffffdde0 ◂— 0x0
*RSP 0x7fffffffd1f0 ◂— 0x0
*RIP 0x2aadf2 (fputs+386) ◂— lea rdx, [rbp - 0x840]
──────────────────────────────────────────────────────────────────────────────────
0x2aadf2 <fputs+386> lea rdx, [rbp - 0x840]
0x2aadf9 <fputs+393> mov rdi, rax
0x2aadfc <fputs+396> mov rsi, r13
0x2aadff <fputs+399> mov rcx, r12
1
2
3
4
5
6
7
8
9
10
11
12
13
14
 RAX  0x2aadf2 (fputs+386) ◂— lea    rdx, [rbp - 0x840]
RBX 0x607000000090 ◂— 0x7363696870617247 ('Graphics')
RCX 0x0
*RDX 0x7fffffffd1f0 ◂— 0x0
RDI 0x232900 (__asan::kInterceptorViaLibrary) ◂— 'interceptor_via_lib'
RSI 0x232900 (__asan::kInterceptorViaLibrary) ◂— 'interceptor_via_lib'
RBP 0x7fffffffda30 —▸ 0x7fffffffdbd0 —▸ 0x7fffffffdc90 —▸ 0x7fffffffdde0 ◂— 0x0
RSP 0x7fffffffd1f0 ◂— 0x0
*RIP 0x2aadf9 (fputs+393) ◂— mov rdi, rax
──────────────────────────────────────────────────────────────────────────────────
0x2aadf2 <fputs+386> lea rdx, [rbp - 0x840]
0x2aadf9 <fputs+393> mov rdi, rax
0x2aadfc <fputs+396> mov rsi, r13
0x2aadff <fputs+399> mov rcx, r12
1
2
3
4
5
pwndbg> telescope 0x7fffffffda30-0x840
00:0000│ rdx rsp 0x7fffffffd1f0 ◂— 0x0
... ↓ 5 skipped
06:00300x7fffffffd220 ◂— 0x1b
07:00380x7fffffffd228 ◂— 0x400
  • 其实这里还不容易看出内存泄露
  • 所以我们用 GDB 在没有 ASan 的 tiffinfo->tif_print.c:127 处打断点,进行调试:
1
2
3
0x289711 <TIFFPrintDirectory+11761>    call   fputs@plt                      <fputs@plt>
s: 0x49edc0 ◂— 0x7363696870617247 ('Graphics')
stream: 0x7ffff7e416a0 (_IO_2_1_stdout_) ◂— 0xfbad2a84
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
pwndbg> telescope 0x49edc0
00:00000x49edc0 ◂— 0x7363696870617247 ('Graphics')
01:00080x49edc8 ◂— 0x31206b636967614d ('Magick 1')
02:00100x49edd0 ◂— 0x6c65726e7520322e ('.2 unrel')
03:00180x49edd8 ◂— 0x3151206465736165 ('eased Q1')
04:00200x49ede0 ◂— 0x2f3a707474682036 ('6 http:/')
05:00280x49ede8 ◂— 0xcccccccccc77772f
06:00300x49edf0 ◂— 0xcccccccccccccccc
07:00380x49edf8 ◂— 0x77cccccccccccccc
08:00400x49ee00 —▸ 0x29232e (tiffFields+2030) —▸ 0x330000 (__afl_area_initial+609936) ◂— 0x0
09:00480x49ee08 ◂— 0x21 /* '!' */
0a:00500x49ee10 ◂— 0x734d6963 /* 'ciMs' */

pwndbg> x/64xb 0x49edc0
0x49edc0: 0x47 0x72 0x61 0x70 0x68 0x69 0x63 0x73
0x49edc8: 0x4d 0x61 0x67 0x69 0x63 0x6b 0x20 0x31
0x49edd0: 0x2e 0x32 0x20 0x75 0x6e 0x72 0x65 0x6c
0x49edd8: 0x65 0x61 0x73 0x65 0x64 0x20 0x51 0x31
0x49ede0: 0x36 0x20 0x68 0x74 0x74 0x70 0x3a 0x2f
0x49ede8: 0x2f 0x77 0x77 0xcc 0xcc 0xcc 0xcc 0xcc
0x49edf0: 0xcc 0xcc 0xcc 0xcc 0xcc 0xcc 0xcc 0xcc
0x49edf8: 0xcc 0xcc 0xcc 0xcc 0xcc 0xcc 0xcc 0x77
0x49ee00: [0x2e] [0x23] [0x29] 0x00 0x00 0x00 0x00 0x00
  • 目标 fputs 执行结果:
1
2
3
4
5
6
pwndbg> c
Continuing.
Tag 7217: GraphicsMagick 1.2 unreleased Q16 http://ww��������������������w.#)
1 Strips:
0: [ 8, 3020]
[Inferior 1 (process 4622) exited normally]
  • 注意 0x49ee00 处:
    • [0x2e]->[.]
    • [0x23]->[#]
    • [0x29]->[)]
  • 这是 0x29232e (tiffFields+2030) 的后3字节,而 fputs 把它打印出来了(内存泄露)

因为 fputs 遇到 \x00 才会停止,这就导致了内存泄露