Exercise 4: LibTIFF

0x01 准备

  我们要做的工作如下:

  1. Fuzz LibTiff (with ASan enabled) until you have a few unique crashes
  2. Triage the crashes to find a PoC for the vulnerability
  3. 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 相关资料:

  最后一个链接写道:

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 的步骤。

  1. fuzz 时使用 -D -j -c -r -s -w 开关。
  2. 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