用libFuzzer搞事情

前面一篇文章介绍过libFuzzer的基本用法了,本文就用libFuzzer来搞一些实际的事情。这里的学习还是以libfuzzer-workshop这个系列教程为主。

Heartbleed Vulnerable(CVE-2014-0160)

编译有漏洞的openssl版本 libfuzzer-workshop/lessons/05

1
2
3
4
5
$ tar xzf openssl1.0.1f.tgz
$ cd openssl1.0.1f
$ ./config
$ make clean
$ make CC="clang -O2 -fno-omit-frame-pointer -g -fsanitize=address -fsanitize-coverage=trace-pc-guard,trace-cmp,trace-gep,trace-div" -j$(nproc)

fuzzer实现

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
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <assert.h>
#include <stdint.h>
#include <stddef.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, CERT_PATH "server.pem",
SSL_FILETYPE_PEM));
assert(SSL_CTX_use_PrivateKey_file(sctx, CERT_PATH "server.key",
SSL_FILETYPE_PEM));
return sctx;
}

extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
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);
BIO_write(sinbio, data, size);
SSL_do_handshake(server);
SSL_free(server);
return 0;
}

fuzzer中用到了刚刚编译的openssl产生的库文件,代码中的API都是openssl库中的。
编译运行fuzzer

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
$ clang++ -g openssl_fuzzer.cc -O2 -fno-omit-frame-pointer -fsanitize=address \
-fsanitize-coverage=trace-pc-guard,trace-cmp,trace-gep,trace-div \
-Iopenssl1.0.1f/include openssl1.0.1f/libssl.a openssl1.0.1f/libcrypto.a \
../../libFuzzer/libFuzzer.a -o openssl_fuzzer
$ mkdir corpus1
$ ./openssl_fuzzer corpus1
INFO: Seed: 4157502167
INFO: Loaded 1 modules (36195 guards): [0xc19890, 0xc3ce1c),
Loading corpus dir: corpus1
INFO: -max_len is not provided, using 64
INFO: A corpus is not provided, starting from an empty corpus
#0 READ units: 1
#1 INITED cov: 1527 ft: 413 corp: 1/1b exec/s: 0 rss: 33Mb
#4 NEW cov: 1533 ft: 444 corp: 2/10b exec/s: 0 rss: 34Mb L: 9 MS: 3 ChangeByte-ChangeBit-CMP- DE: "\xff\xff\xff\xff\xff\xff\xff\xff"-
#111 NEW cov: 1533 ft: 447 corp: 3/28b exec/s: 0 rss: 39Mb L: 18 MS: 5 ShuffleBytes-InsertByte-PersAutoDict-PersAutoDict-ChangeBit- DE: "\xff\xff\xff\xff\xff\xff\xff\xff"-"\xff\xff\xff\xff\xff\xff\xff\xff"-
#1186 NEW cov: 1536 ft: 463 corp: 4/38b exec/s: 0 rss: 84Mb L: 10 MS: 5 CopyPart-ChangeBit-InsertByte-CMP-CMP- DE: "\xff\xff\xff\xff"-"\xfd\x03\x00\x00"-
...
=================================================================
==11010==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x629000009748 at pc 0x0000004c542c bp 0x7ffc92f1bac0 sp 0x7ffc92f1b270
READ of size 65279 at 0x629000009748 thread T0
#0 0x4c542b in __asan_memcpy /home/fanrong/Downloads/src/llvm/projects/compiler-rt/lib/asan/asan_interceptors.cc:462
#1 0x5221b2 in tls1_process_heartbeat /home/fanrong/Computer/Fuzz/libfuzzer-workshop/lessons/05/openssl1.0.1f/ssl/t1_lib.c:2586:3
#2 0x593639 in ssl3_read_bytes /home/fanrong/Computer/Fuzz/libfuzzer-workshop/lessons/05/openssl1.0.1f/ssl/s3_pkt.c:1092:4
#3 0x597f23 in ssl3_get_message /home/fanrong/Computer/Fuzz/libfuzzer-workshop/lessons/05/openssl1.0.1f/ssl/s3_both.c:457:7
#4 0x5604d9 in ssl3_get_client_hello /home/fanrong/Computer/Fuzz/libfuzzer-workshop/lessons/05/openssl1.0.1f/ssl/s3_srvr.c:941:4
#5 0x55c4fc in ssl3_accept /home/fanrong/Computer/Fuzz/libfuzzer-workshop/lessons/05/openssl1.0.1f/ssl/s3_srvr.c:357:9
#6 0x5158ac in LLVMFuzzerTestOneInput /home/fanrong/Computer/Fuzz/libfuzzer-workshop/lessons/05/openssl_fuzzer.cc:39:3
...

如输出所示,tls1_process_heartbeat函数中发生了heap-buffer-overflow。可以看到模糊测试是非常有效的。

c-ares(CVE-2016-5180)

接下来看一下c-ares的漏洞,这个漏洞已经被利用在ChromeOS上,以root权限远程代码执行
编译有漏洞的c-ares版本 libfuzzer-workshop/lessons/06

1
2
3
4
5
$ tar xzvf c-ares.tgz
$ cd c-ares
$ ./buildconf
$ ./configure CC="clang -O2 -fno-omit-frame-pointer -g -fsanitize=address -fsanitize-coverage=trace-pc-guard,trace-cmp,trace-gep,trace-div"
$ make CFLAGS=

fuzzer实现

1
2
3
4
5
6
7
8
9
10
#include <ares.h>

extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
unsigned char *buf;
int buflen;
std::string s(reinterpret_cast<const char *>(data), size);
ares_create_query(s.c_str(), ns_c_in, ns_t_a, 0x1234, 0, &buf, &buflen, 0);
ares_free_string(buf);
return 0;
}

编译运行fuzzer

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
$ clang++ -g c_ares_fuzzer.cc -O2 -fno-omit-frame-pointer -fsanitize=address \
-fsanitize-coverage=trace-pc-guard,trace-cmp,trace-gep,trace-div \
-Ic-ares c-ares/.libs/libcares.a \
../../libFuzzer/libFuzzer.a -o c_ares_fuzzer
$ mkdir corpus1
$ ./c_ares_fuzzer corpus1
INFO: Seed: 4265518985
INFO: Loaded 1 modules (63 guards): [0x7740f0, 0x7741ec),
INFO: -max_len is not provided, using 64
INFO: A corpus is not provided, starting from an empty corpus
#0 READ units: 1
#1 INITED cov: 20 ft: 17 corp: 1/1b exec/s: 0 rss: 26Mb
#3 NEW cov: 22 ft: 22 corp: 2/3b exec/s: 0 rss: 27Mb L: 2 MS: 2 ShuffleBytes-InsertByte-
#5 NEW cov: 25 ft: 29 corp: 3/41b exec/s: 0 rss: 27Mb L: 38 MS: 4 ShuffleBytes-InsertByte-EraseBytes-InsertRepeatedBytes-
#7 NEW cov: 25 ft: 31 corp: 4/105b exec/s: 0 rss: 27Mb L: 64 MS: 1 CrossOver-
#15 NEW cov: 25 ft: 35 corp: 5/145b exec/s: 0 rss: 27Mb L: 40 MS: 4 ShuffleBytes-CrossOver-CopyPart-CMP- DE: "\x00\x00\x00\x00\x00\x00\x00\x00"-
#25 NEW cov: 25 ft: 39 corp: 6/148b exec/s: 0 rss: 27Mb L: 3 MS: 4 InsertByte-ChangeBit-ChangeByte-InsertByte-
#27 NEW cov: 25 ft: 43 corp: 7/152b exec/s: 0 rss: 27Mb L: 4 MS: 1 InsertByte-
...
=================================================================
==34918==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x6040003344f4 at pc 0x000000516534 bp 0x7fff1814e3f0 sp 0x7fff1814e3e8
WRITE of size 1 at 0x6040003344f4 thread T0
#0 0x516533 in ares_create_query /home/fanrong/Computer/Fuzz/libfuzzer-workshop/lessons/06/c-ares/ares_create_query.c:196:3
#1 0x51553f in LLVMFuzzerTestOneInput /home/fanrong/Computer/Fuzz/libfuzzer-workshop/lessons/06/c_ares_fuzzer.cc:16:3
...

关于fuzzing还有很多高级话题,比如,如何改进fuzzer并分析其performance;种子语料库、自定义选项和其他一些因素都非常重要。
reference
https://github.com/Dor1s/libfuzzer-workshop/tree/master/lessons/05