前面一篇文章介绍过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()));
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