Exercise 4: LibTIFF
0x01 准备
我们要做的工作如下:
- Fuzz LibTiff (with ASan enabled) until you have a few unique crashes
- Triage the crashes to find a PoC for the vulnerability
- Measure the code coverage of this PoC
在 Github 上下载到 4.0.4 release,以 ASan 编译。笔者提升了 ESXi 中的配置,现在 fuzzer 拥有 64G 内存,可以缓解一下 ASan 64 位模式下的内存大占用问题。
用 ASan 编译:
export CC=/afl/afl-clang-lto
export CXX=/afl/afl-clang-lto++
export AFL_USE_ASAN=1
./configure
make
LibTIFF 的文档随源码一起分发,在 html
目录中。
阅读文档,得知 libtiff 附带有大量小工具。我们得选择 fuzz 目标。查阅 CVE-2016-9297 相关资料:
- https://www.cvedetails.com/cve/CVE-2016-9297/
- https://www.debian.org/security/2017/dsa-3762
- http://bugzilla.maptools.org/show_bug.cgi?id=2590
最后一个链接写道:
Triggered in libtiff 4.0.6 with AFL and ASAN. Only crashes if I LD_PRELOAD AFL's libdislocator (more info: https://github.com/mirrorer/afl/tree/master/libdislocator).LD_PRELOAD=/root/afl-2.35b/libdislocator/libdislocator.so ./tiffinfo -i test000
TIFFReadDirectoryCheckOrder: Warning, Invalid TIFF directory; tags are not sorted in ascending order.
确定可以 fuzz tiffinfo 这个工具。文档:
实际上,上述 options 不全。程序支持的有:
我们采用 tiffinfo -i xxx
来运行程序。接下来去寻找测试用例。注意到官方有一些用例:
fuzzer :: /work/src/tools » ./tiffinfo -i ../test/images/rgb-3c-16b.tiff 255 ↵
TIFF Directory at offset 0x22baa (142250)
Image Width: 157 Image Length: 151
Bits/Sample: 16
Sample Format: unsigned integer
Compression Scheme: None
Photometric Interpretation: RGB color
Samples/Pixel: 3
Rows/Strip: 8
Planar Configuration: single image plane
Page Number: 0-1
DocumentName: rgb-3c-16b.tiff
Software: GraphicsMagick 1.2 unreleased Q16 http://www.GraphicsMagick.org/
fuzzer :: /work/src/tools »
就用这些样例作为种子。
0x02 fuzz
运行 AFL:
AFL_TMPDIR=/tmp /afl/afl-fuzz -s 123 -i /work/corpus -o /work/output -- ./tiffinfo -i @@
结果:
反转了!tiffinfo 竟是个脚本。读出内容:
#! /bin/bash
# tiffinfo - temporary wrapper script for .libs/tiffinfo
# Generated by libtool (GNU libtool) 2.4.4
#
# The tiffinfo program cannot be directly executed until all the libtool
# libraries that it depends on are installed.
#
# This wrapper script should never be moved out of the build directory.
# If it is, it will not operate correctly.
# ...
原来真正的可执行文件是 .libs/tiffinfo
。开始 fuzz:
/afl/afl-fuzz -s 123 -i /work/corpus -o /work/output -- ./.libs/tiffinfo -i @@
改成多核,1 master, 3 slave:
/afl/afl-fuzz -s 123 -i /work/corpus -o /work/sync -M fuzzer01 -- ./.libs/tiffinfo -i @@
/afl/afl-fuzz -s 123 -i /work/corpus -o /work/sync -S fuzzer02 -- ./.libs/tiffinfo -i @@
/afl/afl-fuzz -s 123 -i /work/corpus -o /work/sync -S fuzzer03 -- ./.libs/tiffinfo -i @@
/afl/afl-fuzz -s 123 -i /work/corpus -o /work/sync -S fuzzer04 -- ./.libs/tiffinfo -i @@
然而,事有不遂人愿者。经过 20h 的四线程 fuzz,AFL 结果如下:
程序发现的执行路径只有三条,这极为怪异。于是开摆,复现 writeup 的步骤。
- fuzz 时使用
-D -j -c -r -s -w
开关。 - configure 时使用
--disable-shared
第一条笔者也考虑过,但虑及这个漏洞的复现无需打开这些开关,担心如果我们打开了这些开关,会 fuzz 出其他漏洞而非 CVE-2016-9297。然而事实上,暴露出更多的路径有助于发现各种各样的漏洞,我们理应让程序触及尽可能多的代码。
采用 --disable-shared
选项显著降低了编译速度。在漫长的编译过程之后,我们拿到了产出,这时候 tiffinfo
就不是个 perl 脚本,而是真正的 ELF 文件了。
/afl/afl-fuzz -s 123 -i /work/corpus -o /work/out2 -- ./tiffinfo -D -j -c -r -s -w @@
现在情况正常了起来。挂会机回来再看。
可见我们成功 fuzz 出了漏洞。
覆盖率
我们使用 lcov 来做覆盖率检测。
apt install lcov
make clean
CFLAGS="--coverage" LDFLAGS="--coverage" ./configure --disable-shared
lcov --zerocounters --directory ./
lcov --capture --initial --directory ./ --output-file app.info
tools/tiffinfo -D -j -c -r -s -w /work/corpus/palette-1c-1b.tiff
genhtml --highlight --legend -output-directory ./html-coverage/ ./app2.info
tools/tiffinfo -D -j -c -r -s -w /work/out2/default/crashes/*
lcov --no-checksum --directory ./ --capture --output-file app3.info