

fuzz实战之honggfuzz | 穷则变,变则通,通则久
source link: https://jinyu00.github.io/2018/06/03/fuzz_with_honggfuzz.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Honggfuzz实战
本文介绍 libfuzzer
和 afl
联合增强版 honggfuzz
.同时介绍利用 honggfuzz
来 fuzz
网络应用服务。
honggfuzz
也是 google
开发的一款 fuzz
. 其设计思路 和 libfuzzer
和 afl
类似 ,感觉就是 libfuzzer
+ afl
的 增强版。
编译
git clone https://github.com/google/honggfuzz.git
cd honggfuzz
honggfuzz
的使用文档在
https://github.com/google/honggfuzz/tree/master/docs
对几条命令做个解释
honggfuzz -f input_dir -z -s -- /usr/bin/djpeg
-f
: 指定初始样本集目录
-z
: 使用编译时的指令插桩信息来 为 样本变异做回馈, 默认选项
-s
: 表示目标程序从stdin
获取输入,即样本数据通过stdin
喂给程序
honggfuzz -f input_dir -- /usr/bin/djpeg ___FILE___
___FILE___
: 类似于afl
的@@
, 表示程序通过文件获取输入,fuzz
过程会被替换为相应的样本文件名
honggfuzz -f input_dir -P -- /usr/bin/djpeg_persistent_mode
-P
: 表示使用persistent
模式
简单示例(libFuzzer模式)
这里用 libfuzzer-workshop
里面的 libxml2
来进行 fuzz
测试
https://github.com/Dor1s/libfuzzer-workshop/tree/master/lessons/08
下载那个 libxml2.tgz
然后解压,用 hfuzz-clang
编译
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
代码
#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
链接)。
/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
/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
#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
捕获到。
/home/haclh/vmdk_kernel/honggfuzz/hfuzz_cc/hfuzz-clang test.c /home/haclh/vmdk_kernel/honggfuzz/libhfuzz/libhfuzz.a -o test
mkdir in
echo 111 > in/1
/home/haclh/vmdk_kernel/honggfuzz/honggfuzz -P -f in/ -W out -- ./test
-P
: 用于开启persistent
模式
马上就能看的 crash
了。
然后会在 out
目录生成触发 crash
的文件
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
类程序
https://github.com/google/honggfuzz/tree/master/libhfnetdriver
用它 fuzz socket
程序很方便, 只要把 main
函数 改成 HFND_FUZZING_ENTRY_FUNCTION
(: 不确定是不是所有的都这样,具体还是得看代码 ,以后再研究研究
int main(int argc, char *argv[]){
......................
......................
......................
HFND_FUZZING_ENTRY_FUNCTION(int argc, char *argv[]){
......................
......................
......................
然后用 hfuzz-clang
编译, 同时用 libhfnetdriver.a
链接即可。
不知道为啥 libmodbus
的 tcp server
用这种方式跑不起来,这里用一个 有漏洞的 socket
程序作为例子,试试这个功能。
vuln.c
#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;
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
/home/haclh/vmdk_kernel/honggfuzz/hfuzz_cc/hfuzz-clang vuln.c /home/haclh/vmdk_kernel/honggfuzz/libhfnetdriver/libhfnetdriver.a -o vuln
关键是要和
libhfnetdriver.a
链接
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
。
参考
本节以 mongoose
为例 实战一波
https://github.com/cesanta/mongoose
这是一个用于 嵌入式网络服务的库, 实现了很多 IOT
中用到的协议。
本节以 http
为例进行 fuzz
。
程序的源代码内有很多的示例 , 其中 examples/simplest_web_server
目录里面是一个很简单的 http server
。
把代码中的 int main
改成 HFND_FUZZING_ENTRY_FUNCTION
。
// 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
链接
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
_HF_TCP_PORT=8000 /home/haclh/vmdk_kernel/honggfuzz/honggfuzz -W oout -f corpus_http1/ -w httpd.wordlist -- ./simplest_web_server
其中用到的样本集 和 字典文件来自
https://github.com/google/honggfuzz/tree/master/examples/apache-httpd
发现了 crash
,下面定位触发漏洞代码, 使用 -fsanitize=address
编译,可以在 触发漏洞时停下来。
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
。
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
行
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
Recommend
-
95
It’s well known that PHP is a dead programming language and that its 22-year-old ecosystem is effectively useless now that we have Node and its fancy new asynchronous frameworks. Node’s superiority…
-
45
文 | 陆水月 近日,关于滴滴的新闻不再是 “ 外卖 ” 。 8 月 8 日,滴滴公司宣布旗下汽车服务平台正式升级为...
-
28
This is a friendly warning that your web-browser does not currently protecting your privacy and/or security as well as you might want. Click on this message to see more information about the issue(s)...
-
74
README.md Description A security oriented, feedback-driven, evolutionary, easy-to-use fuzzer with interesting analysis options. See the
-
20
错误提示页面 抱歉,出错啦! 页面不存在或者已被管理员删除! 返回首页
-
8
当前位置:无极领域 > 随笔 > 正文 格局决定收入,穷则变 变则通 通则久 穷则变,变则通,通则达...
-
7
afl 实战像 libFuzzer, afl 这类 fuzz 对于 从文件 或者 标准输入 获取输入的程序都能进行很好的 fuzz, 但是对于基于网络的程序来说就不是那么方便了。 这篇文章介绍用
-
3
fuzz系列之libfuzzer | 穷则变,变则通,通则久...
-
2
House of Roman 实战...
-
6
openwrt 是一个用于的 路由器 的开源系统。 其他类似的路由器系统相比它的更新速度非常的快,可以看看 github 的更新速度 https://github.com/openwrt/openwrt 感觉以后用到 open...
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK