Exercise 3: TCPdump
任务是 fuzz TCPdump 4.9.2,获得 CVE-2017-13028 漏洞。这是一个越界读取 bug。
0x01 环境准备
在这里找到:TCPdump 4.9.2 版本:
按照 AFL 文档关于使用 ASan 的指引,我们在 32bit 模式下编译。考虑到在一个现有的 debian 系统上添加 i386
架构会面临依赖问题,我们在 docker 中编译 tcpdump:
» docker run --rm -it -v /work:/work afl:20221001 /bin/bash
[afl++ d82435966df4] /work/src/libpcap-1.8.1> export CC=afl-clang-lto
[afl++ d82435966df4] /work/src/libpcap-1.8.1> export CFLAGS="-m32"
[afl++ d82435966df4] /work/src/libpcap-1.8.1> export LDFLAGS="-m32"
[afl++ d82435966df4] /work/src/libpcap-1.8.1> export AFL_USE_ASAN=1
[afl++ d82435966df4] /work/src/libpcap-1.8.1> ./configure
[afl++ d82435966df4] /work/src/libpcap-1.8.1> make
# [+] Instrumented 40265 locations (636 selects) without collisions (10182 collisions have been avoided) (non-hardened, ASAN mode).
[afl++ d82435966df4] /work/src/tcpdump-4.9.2> ./configure
[afl++ d82435966df4] /work/src/tcpdump-4.9.2> make
# [+] Instrumented 117445 locations (3453 selects) without collisions (62828 collisions have been avoided) (non-hardened, ASAN mode).
[afl++ d82435966df4] /work/src/tcpdump-4.9.2> file tcpdump
tcpdump: ELF 32-bit LSB pie executable, Intel 80386,
version 1 (SYSV), dynamically linked,
interpreter /lib/ld-linux.so.2,
for GNU/Linux 3.2.0,
BuildID[xxHash]=50fa5ecc3b933075,
with debug_info, not stripped
[afl++ d82435966df4] /work/src/tcpdump-4.9.2> ldd ./tcpdump
linux-gate.so.1 (0xf7f9b000)
libm.so.6 => /lib32/libm.so.6 (0xf68f8000)
libgcc_s.so.1 => /lib32/libgcc_s.so.1 (0xf7f68000)
libc.so.6 => /lib32/libc.so.6 (0xf66c7000)
/lib/ld-linux.so.2 (0xf7f9d000)
» ./tcpdump -h
tcpdump version 4.9.2
libpcap version 1.8.1
Compiled with AddressSanitizer/CLang.
Usage: tcpdump [-aAbdDefhHIJKlLnNOpqStuUvxX#] [ -B size ] [ -c count ]
[ -C file_size ] [ -E algo:secret ] [ -F file ] [ -G seconds ]
[ -i interface ] [ -j tstamptype ] [ -M secret ] [ --number ]
[ -Q in|out|inout ]
[ -r file ] [ -s snaplen ] [ --time-stamp-precision precision ]
[ --immediate-mode ] [ -T type ] [ --version ] [ -V file ]
[ -w file ] [ -W filecount ] [ -y datalinktype ] [ -z postrotate-command ]
[ -Z user ] [ expression ]
编译完成。
0x02 fuzz
首先,我们很难以去 fuzz TCPdump “从网卡上抓包”这个功能。注意到 TCPdump 可以从 pcap 文件中读取报文,我们考虑由此入手进行 fuzz。
从 exercise 1 的经验,我们应当准备一个足够多样的 seed corpus。可以考虑 TCPdump 自身的测试用例(371 个),以及 wireshark 的测试用例(52 个)。
tests 中 pcap 文件的例子:
很多测试用例需要比较长的时间(>1s)执行,产生报错。我们可以通过 -t
选项指定超时时间。此外,AFL 报告称 seed corpus 太大了。
» AFL_TMPDIR=/tmp /afl/afl-fuzz -s 123 -i /work/corpus -o /work/output -t 200 -- ./tcpdump -r @@
我们将超时时间设为 200ms,然后 fuzz 一晚上。
笑死,一晚上啥也没跑出来。疑似是出问题了。查阅 solution,发现:
- 官方题解采用的是 libpcap 1.8.0 而非我们使用的 1.8.1,还要求选手使用的版本与其一致。
然而,tcpdump 4.9.2 发布于 2017 年 9 月 3 日,与之最接近的 libpcap release 是 2016 年 10 月 25 日的 libpcap 1.8.1;何以舍弃更近的 1.8.1 而选择连 release note 都没有的 1.8.0,笔者蒙在鼓里。 - 官方题解直接在 64bit 系统上编译运行了 64bit 的程序,没有额外的内存限制措施。按照 AFL++ 文档的话,这样做有 Out Of Memory 的风险。
- 官方题解采用的 tcpdump 参数是
-vvvvXX -ee -nn -r
,也就是尽量输出更多的内容。思考之后认为很有道理,因为越多的输出可以经过越多的基本块;此外,可能有某些分析逻辑只有在输出更多内容时才会启用。
笔者选择先执行第三条,采用 -vvv -XX -en
来运行。
仍然没有什么希望。笔者选择将 libpcap 从 1.8.1 降到 1.8.0,但仍然在 32bit 编译。
再次尝试 fuzz。1h 出结果。
跑了一晚上,78 个 unique crash:
0x03 分析
在 PC 上先编译。指令:
export CFLAGS="-fsanitize=address -O0 -g"
export CC=clang-14
./configure
make
复现第一个 crash:
................
subtype GET_IP_MASK_REQUEST, index 3
version 1.0, type VENDOR, length 12062, xid 0x00000020, vendor 0x005c16c7 (Big Switch Networks)
=================================================================
==9758==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x61100000013f at pc 0x5567c7c178a7 bp 0x7ffd8849a6b0 sp 0x7ffd8849a6a8
READ of size 1 at 0x61100000013f thread T0
#0 0x5567c7c178a6 in fn_printn /home/blue/Desktop/workspace/current/src/tcpdump-4.9.2/./util-print.c:185:7
#1 0x5567c7b55904 in of10_bsn_message_print /home/blue/Desktop/workspace/current/src/tcpdump-4.9.2/./print-openflow-1.0.c:899:7
#2 0x5567c7b4f35a in of10_vendor_message_print /home/blue/Desktop/workspace/current/src/tcpdump-4.9.2/./print-openflow-1.0.c:1081:9
#3 0x5567c7b4b235 in of10_header_body_print /home/blue/Desktop/workspace/current/src/tcpdump-4.9.2/./print-openflow-1.0.c:2488:10
#4 0x5567c7b5f5a1 in of_header_body_print /home/blue/Desktop/workspace/current/src/tcpdump-4.9.2/./print-openflow.c:116:10
#5 0x5567c7b5f0bd in openflow_print /home/blue/Desktop/workspace/current/src/tcpdump-4.9.2/./print-openflow.c:141:8
#6 0x5567c7bfa381 in tcp_print /home/blue/Desktop/workspace/current/src/tcpdump-4.9.2/./print-tcp.c:705:17
#7 0x5567c7abc5e1 in ip_print_demux /home/blue/Desktop/workspace/current/src/tcpdump-4.9.2/./print-ip.c:396:3
#8 0x5567c7abfbb4 in ip_print /home/blue/Desktop/workspace/current/src/tcpdump-4.9.2/./print-ip.c:673:3
#9 0x5567c7a8ae35 in ethertype_print /home/blue/Desktop/workspace/current/src/tcpdump-4.9.2/./print-ether.c:333:10
#10 0x5567c7a8a648 in ether_print /home/blue/Desktop/workspace/current/src/tcpdump-4.9.2/./print-ether.c:236:7
#11 0x5567c7a8b34c in ether_if_print /home/blue/Desktop/workspace/current/src/tcpdump-4.9.2/./print-ether.c:261:10
#12 0x5567c7a0421f in pretty_print_packet /home/blue/Desktop/workspace/current/src/tcpdump-4.9.2/./print.c:332:18
#13 0x5567c79efcd8 in print_packet /home/blue/Desktop/workspace/current/src/tcpdump-4.9.2/./tcpdump.c:2497:2
#14 0x5567c7c72fd3 in pcap_offline_read /home/blue/Desktop/workspace/current/src/libpcap-1.8.0/./savefile.c:523:4
#15 0x5567c7c3121e in pcap_loop /home/blue/Desktop/workspace/current/src/libpcap-1.8.0/./pcap.c:904:8
#16 0x5567c79eb85a in main /home/blue/Desktop/workspace/current/src/tcpdump-4.9.2/./tcpdump.c:2000:12
#17 0x7f8c52921d8f in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16
#18 0x7f8c52921e3f in __libc_start_main csu/../csu/libc-start.c:392:3
#19 0x5567c7929824 in _start (/home/blue/Desktop/workspace/current/src/tcpdump-4.9.2/tcpdump+0x1ee824) (BuildId: 24bd888f8773a09fab384a6b0a20e4199112db1f)
0x61100000013f is located 0 bytes to the right of 255-byte region [0x611000000040,0x61100000013f)
allocated by thread T0 here:
#0 0x5567c79ac66e in malloc (/home/blue/Desktop/workspace/current/src/tcpdump-4.9.2/tcpdump+0x27166e) (BuildId: 24bd888f8773a09fab384a6b0a20e4199112db1f)
#1 0x5567c7c756de in pcap_check_header /home/blue/Desktop/workspace/current/src/libpcap-1.8.0/./sf-pcap.c:401:14
#2 0x5567c7c7262b in pcap_fopen_offline_with_tstamp_precision /home/blue/Desktop/workspace/current/src/libpcap-1.8.0/./savefile.c:396:7
#3 0x5567c7c722c1 in pcap_open_offline_with_tstamp_precision /home/blue/Desktop/workspace/current/src/libpcap-1.8.0/./savefile.c:303:6
#4 0x5567c79ea64b in main /home/blue/Desktop/workspace/current/src/tcpdump-4.9.2/./tcpdump.c:1612:8
#5 0x7f8c52921d8f in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16
SUMMARY: AddressSanitizer: heap-buffer-overflow /home/blue/Desktop/workspace/current/src/tcpdump-4.9.2/./util-print.c:185:7 in fn_printn
Shadow bytes around the buggy address:
0x0c227fff7fd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c227fff7fe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c227fff7ff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c227fff8000: fa fa fa fa fa fa fa fa 00 00 00 00 00 00 00 00
0x0c227fff8010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x0c227fff8020: 00 00 00 00 00 00 00[07]fa fa fa fa fa fa fa fa
0x0c227fff8030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c227fff8040: 00 00 00 00 00 00 00 00 00 00 00 fa fa fa fa fa
0x0c227fff8050: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c227fff8060: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c227fff8070: 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
==9758==ABORTING
有趣的是,如果不带 -v
执行这个用例,那么不会产生 crash。