Honggfuzz实战 前言 本文介绍 libfuzzer
和 afl
联合增强版 honggfuzz
.同时介绍利用 honggfuzz
来 fuzz
网络应用服务。
介绍 honggfuzz
也是 google
开发的一款 fuzz
. 其设计思路 和 libfuzzer
和 afl
类似 ,感觉就是 libfuzzer
+ afl
的 增强版。
编译
1
2
3
git clone https://github.com/google/honggfuzz.git
cd honggfuzz
make
honggfuzz
的使用文档在
1
https://github.com/google/honggfuzz/tree/master/docs
对几条命令做个解释
1
honggfuzz -f input_dir -z -s -- /usr/bin/djpeg
-f
: 指定初始样本集目录
-z
: 使用编译时的指令插桩信息来 为 样本变异做回馈, 默认选项
-s
: 表示目标程序从 stdin
获取输入,即样本数据通过 stdin
喂给程序
1
honggfuzz -f input_dir -- /usr/bin/djpeg ___FILE___
___FILE___
: 类似于 afl
的 @@
, 表示程序通过文件获取输入,fuzz
过程会被替换为相应的样本文件名
1
honggfuzz -f input_dir -P -- /usr/bin/djpeg_persistent_mode
-P
: 表示使用 persistent
模式
简单示例(libFuzzer模式) 这里用 libfuzzer-workshop
里面的 libxml2
来进行 fuzz
测试
1
https://github.com/Dor1s/libfuzzer-workshop/tree/master/lessons/08
下载那个 libxml2.tgz
然后解压,用 hfuzz-clang
编译
1
2
3
4
5
6
7
8
tar xvf libxml2.tgz
cd libxml2/
CC=/home/haclh/vmdk_kernel/honggfuzz/hfuzz_cc/hfuzz-clang CXX=/home/haclh/vmdk_kernel/honggfuzz/hfuzz_cc/hfuzz-clang++
export CC=/home/haclh/vmdk_kernel/honggfuzz/hfuzz_cc/hfuzz-clang
export CXX=/home/haclh/vmdk_kernel/honggfuzz/hfuzz_cc/hfuzz-clang++
./autogen.sh
CC=/home/haclh/vmdk_kernel/honggfuzz/hfuzz_cc/hfuzz-clang CXX=/home/haclh/vmdk_kernel/honggfuzz/hfuzz_cc/hfuzz-clang++ ./configure
make -j4
/home/haclh/vmdk_kernel/honggfuzz/
是 honggfuzz
所在的目录
然后会生成 libxml2/.libs/libxml2.a
。
看看 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
#ifdef __cplusplus
extern "C" {
#endif
#include <inttypes.h>
#include "libxml/parser.h"
#include <stdlib.h>
#include <libhfuzz/libhfuzz.h>
FILE* null_file = NULL;
int LLVMFuzzerInitialize(int* argc, char*** argv)
{
null_file = fopen("/dev/null", "w");
return 0;
}
int LLVMFuzzerTestOneInput(const uint8_t* buf, size_t len)
{
xmlDocPtr p = xmlReadMemory((const char*)buf, len, "http://www.google.com", "UTF-8", XML_PARSE_RECOVER | XML_PARSE_NONET);
if (!p) {
return 0;
}
xmlDocFormatDump(null_file, p, 1);
xmlFreeDoc(p);
return 0;
}
#ifdef __cplusplus
}
#endif
和 libfuzzer
的代码基本一致,用 hfuzz-clang
编译(类似于 libfuzzer
, 需要和 libhfuzz.a
链接)。
1
/home/haclh/vmdk_kernel/honggfuzz/hfuzz_cc/hfuzz-clang persistent-xml2.c -Ilibxml2/include libxml2/.libs/libxml2.a /usr/lib/x86_64-linux-gnu/liblzma.a /home/haclh/vmdk_kernel/honggfuzz/libhfuzz/libhfuzz.a -lz -o persistent-xml2
然后运行 fuzz
1
/home/haclh/vmdk_kernel/honggfuzz/honggfuzz -W out -f ~/vmdk_kernel/libfuzzer-workshop-master/lessons/08/corpus2/ -- ./persistent-xml2
-W
: 指定输出目录
-f
: 指定 初始样本集目录
Persistent Fuzzing honggfuzz
还支持 persistent
模式 , 而且他这种模式实现的比 afl
要 更加容易使用。直接上 demo
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
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
#include <inttypes.h>
extern HF_ITER(uint8_t** buf, size_t* len);
void test(char* buf){
if (buf[0] == 'f') {
printf("one\n");
if (buf[1] == 'o') {
printf("two\n");
if (buf[2] == 'o') {
printf("three\n");
if (buf[3] == '!') {
printf("four\n");
abort();
}
}
}
}
}
int main(void) {
for (;;) {
size_t len;
uint8_t *buf;
HF_ITER(&buf, &len);
test(buf);
}
return 0;
}
代码和普通的应用差不多, 需要注意的就是 HF_ITER
,程序可以通过这个宏 来获取 honggfuzz
生成的样本数据 ,这就非常方便了,我们可以把它插入对数据处理的逻辑, 然后循环这段逻辑,就可以不断的进行 fuzz 而不用重新起进程。
对应到上面的代码就是, 一个死循环,循环里面使用 HF_ITER
获取数据,然后把数据喂给我们要测试的逻辑 (这里是 test
函数), 在 test
函数内部 如果满足条件,会触发 abort
模拟一个crash
, 让 honggfuzz
捕获到。
编译
1
/home/haclh/vmdk_kernel/honggfuzz/hfuzz_cc/hfuzz-clang test.c /home/haclh/vmdk_kernel/honggfuzz/libhfuzz/libhfuzz.a -o test
然后运行
1
2
3
mkdir in
echo 111 > in/1
/home/haclh/vmdk_kernel/honggfuzz/honggfuzz -P -f in/ -W out -- ./test
-P
: 用于开启 persistent
模式
马上就能看的 crash
了。
然后会在 out
目录生成触发 crash
的文件
1
2
22:28 haclh@ubuntu:persistent $ xxd out/SIGABRT.PC.7ffff6efa428.STACK.17326c6e5e.CODE.-6.ADDR.\(nil\).INSTR.cmp____\$0xfffffffffffff000\,%rax.fuzz
00000000: 666f 6f21 foo!
内容满足触发 abort
的条件
Honggfuzz NetDriver 介绍 这个是 honggfuzz
自带的一个库 用于 fuzz socket
类程序
1
https://github.com/google/honggfuzz/tree/master/libhfnetdriver
用它 fuzz socket
程序很方便, 只要把 main
函数 改成 HFND_FUZZING_ENTRY_FUNCTION
(: 不确定是不是所有的都这样,具体还是得看代码 ,以后再研究研究
1
2
3
4
5
int main(int argc, char *argv[]){
......................
......................
......................
}
改成
1
2
3
4
5
HFND_FUZZING_ENTRY_FUNCTION(int argc, char *argv[]){
......................
......................
......................
}
然后用 hfuzz-clang
编译, 同时用 libhfnetdriver.a
链接即可。
demo 不知道为啥 libmodbus
的 tcp server
用这种方式跑不起来,这里用一个 有漏洞的 socket
程序作为例子,试试这个功能。
vuln.c
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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
#include <crypt.h>
#include <netdb.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
/* Do nothing with first message */
void handleData0(char *data, int len) {
printf("Auth success\n");
}
/* Second message is stack based buffer overflow */
void handleData1(char *data, int len) {
char buff[8];
bzero(buff, 8);
memcpy(buff, data, len);
printf("Handledata1: %s\n", buff);
}
/* Third message is heap overflow */
void handleData2(char *data, int len) {
char *buff = malloc(8);
bzero(buff, 8);
memcpy(buff, data, len);
printf("Handledata2: %s\n", buff);
free(buff);
}
void handleData3(char *data, int len) {
printf("Meh: %i\n", len);
}
void handleData4(char *data, int len) {
printf("Blah: %i\n", len);
}
void doprocessing(int sock) {
char data[1024];
int n = 0;
int len = 0;
while (1) {
bzero(data, sizeof(data));
len = read(sock, data, 1024);
if (len == 0 || len <= 1) {
return;
}
printf("Received data with len: %i on state: %i\n", len, n);
switch (data[0]) {
case 'A':
handleData0(data, len);
write(sock, "ok", 2);
break;
case 'B':
handleData1(data, len);
write(sock, "ok", 2);
break;
case 'C':
handleData2(data, len);
write(sock, "ok", 2);
break;
case 'D':
handleData3(data, len);
write(sock, "ok", 2);
break;
case 'E':
handleData4(data, len);
write(sock, "ok", 2);
break;
default:
return;
}
n++;
}
}
HFND_FUZZING_ENTRY_FUNCTION(int argc, char *argv[]) {
int sockfd, newsockfd, portno, clilen;
char buffer[256];
struct sockaddr_in serv_addr, cli_addr;
int n, pid;
if (argc == 2) {
portno = atoi(argv[1]);
} else {
portno = 5001;
}
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
perror("ERROR opening socket");
exit(1);
}
int reuse = 1;
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT, (const char *)&reuse, sizeof(reuse)) < 0)
perror("setsockopt(SO_REUSEPORT) failed");
bzero((char *)&serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(portno);
printf("Listening on port: %i\n", portno);
/* Now bind the host address using bind() call.*/
if (bind(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
perror("ERROR on binding");
exit(1);
}
listen(sockfd, 5);
clilen = sizeof(cli_addr);
while (1) {
newsockfd = accept(sockfd, (struct sockaddr *)&cli_addr, &clilen);
if (newsockfd < 0) {
perror("ERROR on accept");
exit(1);
}
printf("New client connected\n");
doprocessing(newsockfd);
printf("Closing...\n");
shutdown(newsockfd, 2);
close(newsockfd);
}
}
就是一个简单的 socket
程序,其中有两个漏洞 一个栈溢出 一个堆溢出, 同时把 int main
改成了 HFND_FUZZING_ENTRY_FUNCTION
。
代码修改自: https://github.com/google/honggfuzz/blob/master/socketfuzzer/vulnserver_cov.c
编译
1
/home/haclh/vmdk_kernel/honggfuzz/hfuzz_cc/hfuzz-clang vuln.c /home/haclh/vmdk_kernel/honggfuzz/libhfnetdriver/libhfnetdriver.a -o vuln
关键是要和 libhfnetdriver.a
链接
运行
1
2
3
4
mkdir in
echo A > in/1
echo B > in/2
_HF_TCP_PORT=5001 /home/haclh/vmdk_kernel/honggfuzz/honggfuzz -f in -- ./vuln
前面三条命令用于生成两个简单样本文件
最后一条命令用于启动 fuzzer
.
_HF_TCP_PORT
指定 server
监听的端口, fuzzer
会和这个端口建立 tcp
连接,然后发送数据。
秒出 crash
。
参考
FUZZING TCP SERVERS
实战 本节以 mongoose
为例 实战一波
1
https://github.com/cesanta/mongoose
这是一个用于 嵌入式网络服务的库, 实现了很多 IOT
中用到的协议。
本节以 http
为例进行 fuzz
。
程序的源代码内有很多的示例 , 其中 examples/simplest_web_server
目录里面是一个很简单的 http server
。
把代码中的 int main
改成 HFND_FUZZING_ENTRY_FUNCTION
。
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
// Copyright (c) 2015 Cesanta Software Limited
// All rights reserved
#include "mongoose.h"
static const char *s_http_port = "8000";
static struct mg_serve_http_opts s_http_server_opts;
static void ev_handler(struct mg_connection *nc, int ev, void *p) {
if (ev == MG_EV_HTTP_REQUEST) {
mg_serve_http(nc, (struct http_message *) p, s_http_server_opts);
}
}
HFND_FUZZING_ENTRY_FUNCTION(void) {
struct mg_mgr mgr;
struct mg_connection *nc;
mg_mgr_init(&mgr, NULL);
printf("Starting web server on port %s\n", s_http_port);
nc = mg_bind(&mgr, s_http_port, ev_handler);
if (nc == NULL) {
printf("Failed to create listener\n");
return 1;
}
// Set up HTTP server parameters
mg_set_protocol_http_websocket(nc);
s_http_server_opts.document_root = "."; // Serve current directory
s_http_server_opts.enable_directory_listing = "yes";
for (;;) {
mg_mgr_poll(&mgr, 1000);
}
mg_mgr_free(&mgr);
return 0;
}
然后用 hfuzz-clang
编译, 注意要和 libhfnetdriver.a
链接
1
2
export HONGGFUZZ_HOME=/home/haclh/vmdk_kernel/honggfuzz
$HONGGFUZZ_HOME/hfuzz_cc/hfuzz-clang simplest_web_server.c ../../mongoose.c -o simplest_web_server -I../.. -DMG_DISABLE_DAV_AUTH -DMG_ENABLE_FAKE_DAVLOCK $HONGGFUZZ_HOME/libhfnetdriver/libhfnetdriver.a -pthread
然后开始 fuzz
1
_HF_TCP_PORT=8000 /home/haclh/vmdk_kernel/honggfuzz/honggfuzz -W oout -f corpus_http1/ -w httpd.wordlist -- ./simplest_web_server
其中用到的样本集 和 字典文件来自
1
https://github.com/google/honggfuzz/tree/master/examples/apache-httpd
发现了 crash
,下面定位触发漏洞代码, 使用 -fsanitize=address
编译,可以在 触发漏洞时停下来。
1
clang -fsanitize=address simplest_web_server.c ../../mongoose.c -o simplest_web_server -g -W -Wall -Werror -I../.. -Wno-unused-function -DMG_DISABLE_DAV_AUTH -DMG_ENABLE_FAKE_DAVLOCK -pthread
然后触发漏洞的样本发送给服务器,就会 crash
。
1
2
3
4
5
6
7
8
01:19 haclh@ubuntu:simplest_web_server $ ./simplest_web_server
Starting web server on port 8000
=================================================================
==16867==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x619000000980 at pc 0x0000004e653d bp 0x7fffb8bab790 sp 0x7fffb8baaf40
READ of size 876 at 0x619000000980 thread T0
#0 0x4e653c in __asan_memcpy /home/haclh/vmdk_kernel/libfuzzer-workshop-master/src/llvm/projects/compiler-rt/lib/asan/asan_interceptors_memintrinsics.cc:23
#1 0x53b9d6 in mbuf_insert /tmp/t/mongoose-6.11/examples/simplest_web_server/../../mongoose.c:1477:24
#2 0x53bafc in mbuf_append /tmp/t/mongoose-6.11/examples/simplest_web_server/../../mongoose.c:1490:10
然后可以定位到 mongoose.c
的 8925
行
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
8923 if (mg_start_process(opts->cgi_interpreter, prog, blk.buf, blk.vars, dir,
8924 fds[1]) != 0) {
8925 size_t n = nc->recv_mbuf.len - (hm->message.len - hm->body.len);
8926 struct mg_connection *cgi_nc =
8927 mg_add_sock(nc->mgr, fds[0], mg_cgi_ev_handler MG_UD_ARG(nc));
8928 struct mg_http_proto_data *cgi_pd = mg_http_get_proto_data(nc);
8929 cgi_pd->cgi.cgi_nc = cgi_nc;
8930 #if !MG_ENABLE_CALLBACK_USERDATA
8931 cgi_pd->cgi.cgi_nc->user_data = nc;
8932 #endif
8933 nc->flags |= MG_F_HTTP_CGI_PARSE_HEADERS;
8934 /* Push POST data to the CGI */
8935 if (n > 0 && n < nc->recv_mbuf.len) {
8936 mg_send(cgi_pd->cgi.cgi_nc, hm->body.p, n);
8937 }
本站文章均原创, 转载注明来源
本文链接: http://blog.hac425.top/2018/06/03/fuzz_with_honggfuzz.html