libxml2:
前言:
本文记录afl-training的第一篇,复现CVE-2015-7497
准备工作:
按照官方操作,本机下面需要设置下临时环境变量
git submodule init && git submodule update
cd libxml2
export CC=afl-clang-fast
./autogen.sh
export AFL_USE_ASAN=1
make -j 4
我们使用了ASAN
,即address sanitizer
,一个快速的内存错误检测工具,可以在内存分配失败时立即退出并打印错误信息,但有的情况下内存分配失败并不会导致程序崩溃
为了方便后面fuzz后处理crash,我们可以开启ASAN
并设置临时环境变量禁用export ASAN_OPTIONS=detect_leaks=0
不过开启ASAN
之后编译的测试程序./testModule
运行时报错
fk@fk-pop:/home/fk/文档/fuzzing/afl-training/challenges/libxml2/libxml2# ./testModule
[-] FATAL: forkserver is already up, but an instrumented dlopen() library loaded afterwards. You must AFL_PRELOAD such libraries to be able to fuzz them or LD_PRELOAD to run outside of afl-fuzz.
To ignore this set AFL_IGNORE_PROBLEMS=1 but this will lead to ambiguous coverage data.
In addition, you can set AFL_IGNORE_PROBLEMS_COVERAGE=1 to ignore the additional coverage instead is going to be easy to
fuzz, is definitely exposed to user-provided input in many situations, and is at high risk of containing bugs (parsing a
complex data format in an unsafe language).
This functionality is exposed in the [parser](http://xmlsoft.org/html/libxml-parser.html) API, and whilst you could dig
through this documentation, the easiest approach is to look at an example.
[`parse1.c`](http://xmlsoft.org/examples/parse1.c) (also in the (use with caution!).
有人提过类似的issue https://coder.social/antonio-morales/fuzzing101/issues/21
追溯到官方FAQ有两种解决方案,一种是设置临时环境变量
export AFL_PRELOAD=foo.so
但是笔者设置之后仍然报错,且在本机检索后并未找到foo.so
(foo.so
其实并不是什么东西,foo
的本意就是无意义,得strace附加进程上看相关的动态链接库
第二种设置临时环境变量忽略后测试成功
export AFL_IGNORE_PROBLEMS=1
编写harness:
以上我们并未生成fuzz所需的入口程序,根据官方文档所说,即是我们需要一个fuzzing harness
,笔者也是第一次,所以跟着官方文档的hint走了
根据官方文档描述libxml2
的api很多,但是最容易进行模糊测试的是核心XML解析逻辑,因为它暴露在用户输入
的情况下,可能在处理不安全的复杂数据时出现bug
根据官方给的examplehttp://xmlsoft.org/examples/parse1.c可以自己大概改一个,从命令行读入文件名的harness
#include <stdio.h>
#include <libxml/parser.h>
#include <libxml/tree.h>
int main(int argc, char **argv) {
if (argc != 2)
return(1);
xmlDocPtr doc; /* the resulting document tree */
doc = xmlReadFile(argv[1], NULL, 0);
if (doc == NULL) {
fprintf(stderr, "Failed to parse %s\n", argv[1]);
return 0;
}
xmlFreeDoc(doc);
/*
* Cleanup function for the XML library.
*/
xmlCleanupParser();
return(0);
}
使用以下指令编译
afl-clang-fast ./fuzz.c -I ./include ./.libs/libxml2.a -lz -lm -g -o fuzzer
#-I 指定include目录和libxml2的静态链接库
#-lm使用math库,-lz使用zlib库,-g开启debug模式
报错
[-] PROGRAM ABORT : Custom ASAN_OPTIONS set without abort_on_error=1 - please fix!
Location : check_asan_opts(), src/afl-fuzz-init.c:2651
查询相关issue:https://groups.google.com/g/afl-users/c/XaE06yzKOu8
再次设置环境变量
export ASAN_OPTIONS=abort_on_error=1:symbolize=0
可以正常fuzz
优化harness:
但是太慢了,我们根据afl++文档可以添加一点优化
注意不必要语句的删除,下面是失败示例
#include <stdio.h>
#include <libxml/parser.h>
#include <libxml/tree.h>
int main(int argc, char **argv) {
if (argc != 2)
return(1);
xmlDocPtr doc; /* the resulting document tree */
#ifndef __AFL_HAVE_MANUAL_CONTROL
__AFL_INIT();
#endif
while(__AFL_LOOP(1000)){
doc = xmlReadFile(argv[1], NULL, 0);
if (doc == NULL) {
fprintf(stderr, "Failed to parse %s\n", argv[1]);
return 0;
}
xmlFreeDoc(doc);
}
/*
* Cleanup function for the XML library.
*/
xmlCleanupParser();
return(0);
}
成功示例,注意两者区别:
#include <stdio.h>
#include <libxml/parser.h>
#include <libxml/tree.h>
int main(int argc, char **argv)
{
if (argc != 2)
return (1);
xmlDocPtr doc; /* the resulting document tree */
#ifndef __AFL_HAVE_MANUAL_CONTROL
__AFL_INIT();
#endif
while (__AFL_LOOP(1000))
{
doc = xmlReadFile(argv[1], NULL, 0);
if (doc != NULL)
{
xmlFreeDoc(doc);
}
}
/*
* Cleanup function for the XML library.
*/
xmlCleanupParser();
return (0);
}
最后效率提升最高30x
crash分析:
4个小时8个unique crash
使用afl-tmin
精简input:
for i in id*; do afl-tmin -i $i -o ./tmp/$i -m none -- ./fuzzer @@; done;
../fuzzer 输入文件
查看报错信息,发现有几个输入文件错误发生在parser.c
的第10666行,即错误发生在宏MOVETO_ENDTAG();
中,并不是我们想要复现的xmlDictComputeFastQKey
函数
使用gdb调试跟进确认错误出现在宏定义处,并不是我们想要复现的错误
好在有一个输入样本能触发CVE中的错误
fk@fk-pop:~/文档/fuzzing/afl-training/challenges/libxml2/libxml2/opt/default/crashes/tmp$ hexdump -C id\:000002\,sig\:06\,src\:002889\,time\:8672911\,execs\:11122815\,op\:havoc\,rep\:16
00000000 3c 4a 30 30 30 30 30 30 30 30 30 30 30 30 30 30 |<J00000000000000|
00000010 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 |0000000000000000|
*
00000350 30 30 3a 4a 30 30 30 30 30 30 30 30 30 30 30 30 |00:J000000000000|
00000360 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 |0000000000000000|
*
000003b0 30 30 30 30 30 30 30 30 30 30 |0000000000|
000003ba
即xmlDictComputeFastQKey
函数中的溢出
=================================================================
==3397754==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x62100000123c at pc 0x556c7cb6c531 bp 0x7ffe76acfde0 sp 0x7ffe76acfdd8
READ of size 1 at 0x62100000123c thread T0
#0 0x556c7cb6c530 in xmlDictComputeFastQKey /home/fk/文档/fuzzing/afl-training/challenges/libxml2/libxml2/dict.c:489:18
#1 0x556c7cb6a16f in xmlDictQLookup /home/fk/文档/fuzzing/afl-training/challenges/libxml2/libxml2/dict.c:1093:12
#2 0x556c7cb79590 in xmlSAX2StartElementNs /home/fk/文档/fuzzing/afl-training/challenges/libxml2/libxml2/SAX2.c:2238:17
binary ninja查看调用xmlDictComputeFastQKey
函数的偏移
gdb断点打过去:
gdb --args ../fuzzer id\:000002\,sig\:06\,src\:002889\,time\:8672911\,execs\:11122815\,op\:havoc\,rep\:16
b *$rebase(0x03de16b)
c
可以得到相关寄存器值
► 0x55555593216b <xmlDictQLookup+235> call xmlDictComputeFastQKey <xmlDictComputeFastQKey>
rdi: 0x6190000000d7 ◂— 0x303030303030304a ('J0000000')
rsi: 0x351
rdx: 0x621000001528 ◂— 'J000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'
rcx: 0x67
r8: 0x2e70efeb
根据dict.c
中函数传参规则,plen对应$rsi
,len对应$rcx
len-(plen+1+1)
即0x67-(0x351+0x1+0x1)=-0x2EC
下标负数,数组越界
static unsigned long
xmlDictComputeFastQKey(const xmlChar *prefix, int plen,
const xmlChar *name, int len, int seed)
{
unsigned long value = (unsigned long) seed;
if (plen == 0)
value += 30 * (unsigned long) ':';
else
value += 30 * (*prefix);
if (len > 10) {
value += name[len - (plen + 1 + 1)];
len = 10;
if (plen > 10)
plen = 10;
}
switch (plen) {
case 10: value += prefix[9];
case 9: value += prefix[8];
case 8: value += prefix[7];
case 7: value += prefix[6];
case 6: value += prefix[5];
case 5: value += prefix[4];
case 4: value += prefix[3];
case 3: value += prefix[2];
case 2: value += prefix[1];
case 1: value += prefix[0];
default: break;
}
len -= plen;
if (len > 0) {
value += (unsigned long) ':';
len--;
}
switch (len) {
case 10: value += name[9];
case 9: value += name[8];
case 8: value += name[7];
case 7: value += name[6];
case 6: value += name[5];
case 5: value += name[4];
case 4: value += name[3];
case 3: value += name[2];
case 2: value += name[1];
case 1: value += name[0];
default: break;
}
return(value);
}
heartbleed:
前言:
本文记录afl-training的第二篇,复现心脏滴血漏洞
准备工作:
设置环境变量:
export CC=afl-clang-fast CXX=afl-clang-fast++ AFL_USE_ASAN=1
然后:
./config -d
make
在原来的handshake.cc
上作修改,在BIO_write
前初始化size
和data
:
// Copyright 2016 Google Inc. All Rights Reserved.
// Licensed under the Apache License, Version 2.0 (the "License");
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <assert.h>
#include <stdint.h>
#include <stddef.h>
#include <unistd.h>
#ifndef CERT_PATH
# define CERT_PATH
#endif
SSL_CTX *Init() {
SSL_library_init();
SSL_load_error_strings();
ERR_load_BIO_strings();
OpenSSL_add_all_algorithms();
SSL_CTX *sctx;
assert (sctx = SSL_CTX_new(TLSv1_method()));
/* These two file were created with this command:
openssl req -x509 -newkey rsa:512 -keyout server.key \
-out server.pem -days 9999 -nodes -subj /CN=a/
*/
assert(SSL_CTX_use_certificate_file(sctx, "server.pem",
SSL_FILETYPE_PEM));
assert(SSL_CTX_use_PrivateKey_file(sctx, "server.key",
SSL_FILETYPE_PEM));
return sctx;
}
int main() {
static SSL_CTX *sctx = Init();
SSL *server = SSL_new(sctx);
BIO *sinbio = BIO_new(BIO_s_mem());
BIO *soutbio = BIO_new(BIO_s_mem());
SSL_set_bio(server, sinbio, soutbio);
SSL_set_accept_state(server);
/* TODO: To spoof one end of the handshake, we need to write data to sinbio
* here */
#ifdef __AFL_HAVE_MANUAL_CONTROL
__AFL_INIT();
#endif
uint8_t data[100] = {0};
size_t size = read(STDIN_FILENO, data, 100);
if (size == -1) {
printf("Failed to read from stdin\n");
return(-1);
}
BIO_write(sinbio, data, size);
SSL_do_handshake(server);
SSL_free(server);
return 0;
}
编译:
afl-clang-fast++ ./handshake.cc -I ./include ./libssl.a ./libcrypto.a -ldl -g -o handshake
Fuzzing:
ipt
中生成随机种子:
cd ipt && dd if=/dev/urandom of=1.in bs=64 count=10
开始fuzz前注意将server.key server.pem
放在和handshake
同目录下
afl-fuzz -i ipt/ -o opt/ -m none ./handshake
crash分析:
将handshake
移至crashes
目录下使用afl-tmin
简化input
for i in id*;do afl-tmin -i $i -o ./tmp/$i -- ./handshake;done;
之后使用简化后的input
测试
fk@fk-pop:~/文档/fuzzing/afl-training/challenges/heartbleed/openssl/opt/default/crashes/tmp$ gdb handshake
GNU gdb (Ubuntu 12.1-0ubuntu1~22.04) 12.1
Copyright (C) 2022 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
pwndbg: loaded 147 pwndbg commands and 46 shell commands. Type pwndbg [--shell | --all] [filter] for a list.
pwndbg: created $rebase, $ida GDB functions (can be used with print/break)
Reading symbols from handshake...
------- tip of the day (disable with set show-tips off) -------
Disable Pwndbg context information display with set context-sections ''
pwndbg> ls
handshake id:000006,sig:06,src:000236,time:22339456,execs:17321069,op:havoc,rep:7
id:000000,sig:06,src:000031,time:54840,execs:42018,op:havoc,rep:1 id:000007,sig:06,src:000233,time:23634783,execs:18301688,op:havoc,rep:3
id:000001,sig:06,src:000024+000075,time:4119745,execs:3210038,op:splice,rep:17 id:000008,sig:06,src:000294+000226,time:25859527,execs:19982226,op:splice,rep:2
id:000002,sig:06,src:000053+000041,time:5079793,execs:3956550,op:splice,rep:4 id:000009,sig:06,src:000270,time:26541712,execs:20500629,op:havoc,rep:1
id:000003,sig:06,src:000024+000068,time:10115665,execs:7871015,op:splice,rep:12 server.key
id:000004,sig:06,src:000040+000006,time:16647584,execs:12944780,op:splice,rep:4 server.pem
id:000005,sig:06,src:000224,time:22158868,execs:17184882,op:havoc,rep:4
This command is deprecated in Pwndbg. Please use the GDB's built-in syntax for running shell commands instead: !ls <args>
pwndbg> run < id:000000,sig:06,src:000031,time:54840,execs:42018,op:havoc,rep:1
Starting program: /home/fk/文档/fuzzing/afl-training/challenges/heartbleed/openssl/opt/default/crashes/tmp/handshake < id:000000,sig:06,src:000031,time:54840,execs:42018,op:havoc,rep:1
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
=================================================================
==7701==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x629000009748 at pc 0x5555556f96d7 bp 0x7fffffffd4d0 sp 0x7fffffffcca0
READ of size 25648 at 0x629000009748 thread T0
[Attaching after Thread 0x7ffff7e99800 (LWP 7701) fork to child process 7704]
[New inferior 2 (process 7704)]
[Detaching after fork from parent process 7701]
[Inferior 1 (process 7701) detached]
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
process 7704 is executing new program: /usr/lib/llvm-14/bin/llvm-symbolizer
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
#0 0x5555556f96d6 in __asan_memcpy (/home/fk/文档/fuzzing/afl-training/challenges/heartbleed/openssl/opt/default/crashes/tmp/handshake+0x1a56d6) (BuildId: 2eb1559a07e34eae87f86fae3812f29a9b98a641)
#1 0x555555744ca1 in tls1_process_heartbeat /home/fk/文档/fuzzing/afl-training/challenges/heartbleed/openssl/ssl/t1_lib.c:2586:3
#2 0x5555557b9129 in ssl3_read_bytes /home/fk/文档/fuzzing/afl-training/challenges/heartbleed/openssl/ssl/s3_pkt.c:1092:4
#3 0x5555557bdcd9 in ssl3_get_message /home/fk/文档/fuzzing/afl-training/challenges/heartbleed/openssl/ssl/s3_both.c:457:7
#4 0x555555786696 in ssl3_get_client_hello /home/fk/文档/fuzzing/afl-training/challenges/heartbleed/openssl/ssl/s3_srvr.c:941:4
#5 0x5555557823ec in ssl3_accept /home/fk/文档/fuzzing/afl-training/challenges/heartbleed/openssl/ssl/s3_srvr.c:357:9
#6 0x555555737a7f in main /home/fk/文档/fuzzing/afl-training/challenges/heartbleed/openssl/./handshake.cc:53:3
#7 0x7ffff7829d8f in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16
#8 0x7ffff7829e3f in __libc_start_main csu/../csu/libc-start.c:392:3
#9 0x555555677564 in _start (/home/fk/文档/fuzzing/afl-training/challenges/heartbleed/openssl/opt/default/crashes/tmp/handshake+0x123564) (BuildId: 2eb1559a07e34eae87f86fae3812f29a9b98a641)
0x629000009748 is located 0 bytes to the right of 17736-byte region [0x629000005200,0x629000009748)
allocated by thread T0 here:
#0 0x5555556fa3ae in malloc (/home/fk/文档/fuzzing/afl-training/challenges/heartbleed/openssl/opt/default/crashes/tmp/handshake+0x1a63ae) (BuildId: 2eb1559a07e34eae87f86fae3812f29a9b98a641)
#1 0x5555557f0ee9 in CRYPTO_malloc /home/fk/文档/fuzzing/afl-training/challenges/heartbleed/openssl/crypto/mem.c:308:8
SUMMARY: AddressSanitizer: heap-buffer-overflow (/home/fk/文档/fuzzing/afl-training/challenges/heartbleed/openssl/opt/default/crashes/tmp/handshake+0x1a56d6) (BuildId: 2eb1559a07e34eae87f86fae3812f29a9b98a641) in __asan_memcpy
Shadow bytes around the buggy address:
0x0c527fff9290: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c527fff92a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c527fff92b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c527fff92c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c527fff92d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x0c527fff92e0: 00 00 00 00 00 00 00 00 00[fa]fa fa fa fa fa fa
0x0c527fff92f0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c527fff9300: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c527fff9310: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c527fff9320: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c527fff9330: 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
==7701==ABORTING
[Inferior 2 (process 7704) exited normally]
定位到t1_lib.c
中的tls1_process_heartbeat
的memcpy(bp, pl, payload);
即是触发心脏滴血的关键代码
*RIP 0x555555744c9d (tls1_process_heartbeat+765) ◂— call 0x5555556f9520
────────────────────────────────────────────────────────────────────────────────────────────────────────────[ DISASM / x86-64 / set emulate on ]─────────────────────────────────────────────────────────────────────────────────────────────────────────────
0x555555744c89 <tls1_process_heartbeat+745> jne tls1_process_heartbeat+1249 <tls1_process_heartbeat+1249>
0x555555744c8f <tls1_process_heartbeat+751> mov byte ptr [rbx + 2], r12b
0x555555744c93 <tls1_process_heartbeat+755> lea rdi, [rbx + 3]
0x555555744c97 <tls1_process_heartbeat+759> mov rsi, r15
0x555555744c9a <tls1_process_heartbeat+762> mov rdx, rbp
► 0x555555744c9d <tls1_process_heartbeat+765> call __asan_memcpy <__asan_memcpy>
rdi: 0x62b000000203 ◂— 0xbebebebebebebebe
rsi: 0x62900000520b ◂— 0xbebebebebebebebe
rdx: 0x6430
rcx: 0x55555662f720 (__afl_area_initial) ◂— 0x1000000000002
0x555555744ca2 <tls1_process_heartbeat+770> lea rdi, [rbx + rbp]
0x555555744ca6 <tls1_process_heartbeat+774> add rdi, 3
0x555555744caa <tls1_process_heartbeat+778> mov esi, 0x10
0x555555744caf <tls1_process_heartbeat+783> call RAND_pseudo_bytes <RAND_pseudo_bytes>
0x555555744cb4 <tls1_process_heartbeat+788> mov rdi, qword ptr [rsp + 0x10]
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[ SOURCE (CODE) ]──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
In file: /home/fk/文档/fuzzing/afl-training/challenges/heartbleed/openssl/ssl/t1_lib.c
2581 bp = buffer;
2582
2583 /* Enter response type, length and copy payload */
2584 *bp++ = TLS1_HB_RESPONSE;
2585 s2n(payload, bp);
► 2586 memcpy(bp, pl, payload);
2587 bp += payload;
2588 /* Random padding */
2589 RAND_pseudo_bytes(bp, padding);
2590
2591 r = ssl3_write_bytes(s, TLS1_RT_HEARTBEAT, buffer, 3 + payload + padding);
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[ STACK ]──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
00:0000│ rsp 0x7fffffffd4e0 —▸ 0xc34000001b0 ◂— 0x0
01:0008│ 0x7fffffffd4e8 —▸ 0x6180000000f0 ◂— 0x0
02:0010│ 0x7fffffffd4f0 —▸ 0x618000000080 ◂— 0x200000000301
03:0018│ 0x7fffffffd4f8 ◂— 0x644300005203
04:0020│ 0x7fffffffd500 —▸ 0x618000000118 ◂— 0x0
05:0028│ 0x7fffffffd508 —▸ 0x7fffffffd640 ◂— 0x3fff800
06:0030│ 0x7fffffffd510 —▸ 0x7fffffffd540 ◂— 0x41b58ab3
07:0038│ 0x7fffffffd518 —▸ 0xc34000001b6 ◂— 0x0
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
► 0 0x555555744c9d tls1_process_heartbeat+765
1 0x5555557b912a ssl3_read_bytes+13306
2 0x5555557bdcda ssl3_get_message+538
3 0x555555786697 ssl3_get_client_hello+535
4 0x5555557823ed ssl3_accept+3405
5 0x555555737a80 main+448
6 0x7ffff7829d90 __libc_start_call_main+128
7 0x7ffff7829e40 __libc_start_main+128
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
pwndbg> ni
=================================================================
==8348==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x629000009748 at pc 0x5555556f96d7 bp 0x7fffffffd4d0 sp 0x7fffffffcca0
READ of size 25648 at 0x629000009748 thread T0
[Attaching after Thread 0x7ffff7e99800 (LWP 8348) fork to child process 8359]
[New inferior 2 (process 8359)]
[Detaching after fork from parent process 8348]
[Inferior 1 (process 8348) detached]
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
process 8359 is executing new program: /usr/lib/llvm-14/bin/llvm-symbolizer
Warning:
Cannot insert breakpoint 2.
Cannot access memory at address 0x5555557b9125
sendmail_1301:
前言:
本文为afl-training的第三篇,针对sendmail
的fuzz,比较了一下普通harness普通fuzz与并行fuzz的效率提升,以及普通harness与优化过后的harness进行fuzz时的效率差别
准备工作:
这道题已经准备好了harness
根据makefile
:
CFLAGS ?= -g
m1-bad: mime1-bad.c main.c
${CC} ${CFLAGS} -o m1-bad mime1-bad.c main.c -I .
clean:
rm -f *-bad *-ok
我们设置环境变量export CC=afl-clang-fast
后make即可
生成m1-bad
,测试一下能正常运行
fk@fk-pop:~/文档/fuzzing/afl-training/challenges/sendmail/1301$ ./m1-bad 1.txt
buf-obuf=64
obp-obuf=0
canary-obuf=4294967268
canary = GOOD
obuf = 1
canary should be GOOD
canary = GOOD
Fuzzing:
建好文件夹和生成随机种子之后开始fuzz
afl-fuzz -i ipt/ -o opt/ -- ./m1-bad @@
没有修改过的harness
编译出的进行fuzz时表现如下:
在3min左右的时候跑出了crash;
然后我们使用并行fuzz:
afl-fuzz -i ipt1/ -o opt1/ -M f1 -- ./m1-bad @@
afl-fuzz -i ipt1/ -o opt1/ -S f2 -- ./m1-bad @@
afl-fuzz -i ipt1/ -o opt1/ -S f3 -- ./m1-bad @@
afl-fuzz -i ipt1/ -o opt1/ -S f4 -- ./m1-bad @@
这次我们不到1min主fuzzer就跑出了crash:
最后我们测试一下延迟初始化和持久化模式需要多久能跑出,修改一下harness:
#include "my-sendmail.h"
#include <assert.h>
int main(int argc, char **argv)
{
HDR *header;
register ENVELOPE *e;
FILE *temp;
#ifdef __AFL_HAVE_MANUAL_CONTROL
__AFL_INIT();
#endif
while(__AFL_LOOP(1000))
{
assert(argc == 2);
temp = fopen(argv[1], "r");
assert(temp != NULL);
header = (HDR *)malloc(sizeof(struct header));
header->h_field = "Content-Transfer-Encoding";
header->h_value = "quoted-printable";
header->h_link = NULL;
header->h_flags = 0;
e = (ENVELOPE *)malloc(sizeof(struct envelope));
e->e_id = "First Entry";
e->e_dfp = temp;
mime7to8(header, e);
fclose(temp);
free(e);
free(header);
return 0;
}
}
重新make后进行fuzz:
在2min30s左右跑出crash,相较于最原始的harness还是有效率提升的
crash分析:
使用afl-tmin
精简样本:
for i in id*;do afl-tmin -i $i -o ./tmp/$i -- ./m1-bad @@;done;
得到简化后样本
fk@fk-pop:~/文档/fuzzing/afl-training/challenges/sendmail/1301/opt1/f1/crashes/tmp$ hexdump -C id\:000000\,sig\:11\,src\:000038\,time\:3687\,execs\:21373\,op\:havoc\,rep\:1
00000000 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 |0000000000000000|
*
00000230 30 30 30 30 30 30 30 30 30 30 30 3d 0a 30 30 30 |00000000000=.000|
00000240 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 |0000000000000000|
*
00000260 30 30 30 3d 0a 30 30 30 30 30 30 |000=.000000|
0000026b
后用AFL_USE_ASAN=1
的编译选项重新编译了一份之后跑样本确定crash发生处确实与给出的mime1-bad.c
中注释BAD
的地方吻合
==2927476==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7fffffffdc92 at pc 0x555555631800 bp 0x7fffffffdbb0 sp 0x7fffffffdba8
WRITE of size 1 at 0x7fffffffdc92 thread T0
[Attaching after Thread 0x7ffff7e9cc40 (LWP 2927476) fork to child process 2927479]
[New inferior 2 (process 2927479)]
[Detaching after fork from parent process 2927476]
[Inferior 1 (process 2927476) detached]
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
process 2927479 is executing new program: /usr/lib/llvm-14/bin/llvm-symbolizer
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
#0 0x5555556317ff in mime_fromqp /home/fk/文档/fuzzing/afl-training/challenges/sendmail/1301/mime1-bad.c:248:18
#1 0x5555556317ff in mime7to8 /home/fk/文档/fuzzing/afl-training/challenges/sendmail/1301/mime1-bad.c:156:7
#2 0x5555556323c6 in main /home/fk/文档/fuzzing/afl-training/challenges/sendmail/1301/main.c:94:11
#3 0x7ffff7c29d8f in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16
#4 0x7ffff7c29e3f in __libc_start_main csu/../csu/libc-start.c:392:3
#5 0x5555555733f4 in _start (/home/fk/文档/fuzzing/afl-training/challenges/sendmail/1301/opt/default/crashes/m1-bad+0x1f3f4) (BuildId: 774480b07edb24c4f93faf62426d5928c809433f)
小结:
以上为afl-training几个具有代表性的练习,帮助熟悉afl++的fuzz流程,现整合为1篇