Honggfuzz实战

前言

本文介绍 libfuzzer 和 afl 联合增强版 honggfuzz .同时介绍利用 honggfuzz 来 fuzz 网络应用服务。

介绍

honggfuzz 也是 google 开发的一款 fuzz . 其设计思路 和 libfuzzer 和 afl 类似 ,感觉就是 libfuzzer + afl 的 增强版。

编译

git clone https://github.com/google/honggfuzz.git
cd honggfuzz
make

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 链接即可。

demo

不知道为啥 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;
} 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

编译

/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 。

参考

FUZZING TCP SERVERS

实战

本节以 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 }

fuzz实战之honggfuzz的更多相关文章

  1. 转:Fuzzing Apache httpd server with American Fuzzy Lop + persistent mode

    Fuzzing Apache httpd server with American Fuzzy Lop + persistent mode 小结:AFL主要以文件作为输入进行fuzz,本文介绍如何对网 ...

  2. OD: Windows Driver Fuzz

    内核 FUZZ 思路 内核 API  函数:是提供给 Ring3 调用,在 Ring0 完成最终功能的函数.这些函数接收 Ring3 传入的参数,如果处理参数的过程存在问题的话,很有可能成为一个内核漏 ...

  3. fuzz系列之afl

    afl 实战 前言 像 libFuzzer, afl 这类 fuzz 对于 从文件 或者 标准输入 获取输入的程序都能进行很好的 fuzz, 但是对于基于网络的程序来说就不是那么方便了. 这篇文章介绍 ...

  4. fuzz系列之libfuzzer

    前言 本文以 libfuzzer-workshop 为基础 介绍 libFuzzer 的使用. libFuzzer简介 libFuzzer 是一个in-process,coverage-guided, ...

  5. [红日安全]Web安全Day5 - 任意文件上传实战攻防

    本文由红日安全成员: MisakiKata 编写,如有不当,还望斧正. 大家好,我们是红日安全-Web安全攻防小组.此项目是关于Web安全的系列文章分享,还包含一个HTB靶场供大家练习,我们给这个项目 ...

  6. moviepy音视频剪辑基类VideoClip的write_gif方法opt、fuzz、dispose、colors、loop等参数的作用

    ☞ ░ 前往老猿Python博文目录 ░ moviepy音视频剪辑模块的视频剪辑基类write_gif方法用于将视频剪辑输出到gif文件,调用语法如下: def write_gif(self, fil ...

  7. PyQt+moviepy音视频剪辑实战文章目录

    ☞ ░ 前往老猿Python博文目录 ░ 本专栏为moviepy音视频剪辑合成相关内容介绍的免费专栏,对应的收费专栏为<moviepy音视频开发专栏>. 一.moviepy基础能力系统介绍 ...

  8. Vulnhub实战-grotesque3靶机👻

    Vulnhub实战-grotesque3靶机 靶机地址:http://www.vulnhub.com/entry/grotesque-301,723/ 1.靶机描述 2.主机探测,端口扫描 我们在vm ...

  9. Vulnhub实战-FALL靶机👻

    Vulnhub实战-FULL靶机 下载地址:http://www.vulnhub.com/entry/digitalworldlocal-fall,726/ 1.描述 通过描述我们可以知道这个靶机枚举 ...

随机推荐

  1. error 'there is already an open datareader associated with this command which must be closed first'

    This can be easily solved by allowing MARS in your connection string. Add MultipleActiveResultSets=t ...

  2. 使用TopShelf做windows服务

    class Program { static void Main(string[] args) { HostFactory.Run(x => { x.RunAsLocalSystem(); x. ...

  3. Java之集合(八)HashMap

    转载请注明源出处:http://www.cnblogs.com/lighten/p/7338372.html 1.前言 本章介绍Java中最常用的一个集合类HashMap,此类在不同的JDK版本有不同 ...

  4. 2018春招-今日头条笔试题-第四题(python)

    题目描述:2018春招-今日头条笔试题5题(后附大佬答案-c++版) #-*- coding:utf-8 -*- class Magic: ''' a:用于存储数组a b:用于存储数组b num:用于 ...

  5. java内存数据管理

    准确的说应该是java8以前的内存管理方式 区别在永久代(方法区)上 public class RamManager { //1.a存储于永久代 public static int a =1; pri ...

  6. Spring Security构建Rest服务-0100-前言

    一.我的前言 这是看慕课网老师讲的SpringSecurity的学习笔记,老师讲的很好,开篇就说到了我的心里,老师说道: 有一定经验的程序员如何提升自己? 1,每天都很忙,但是感觉水平没有提升 2,不 ...

  7. java数据结构之队列

    队列概述队列是一种特殊的线性表,它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作.进行插入操作的端称为队尾,进行删除操作的端称为队头.队列中没有元素时,称为空队列.– ...

  8. 使用 Chrome 生成 UUID

    打开Chrome,键入 Ctrl + Shift + J,打开 JS 控制台,然后输入下面的JS代码: function getGUID( ) { function _p8(s) { var p = ...

  9. 分析org.rpgpoet.Music.wizards.length

    例子如下: package bazola; public class Gabriel { static int n = org.rpgpoet.Music.wizards.length; } pack ...

  10. tomcat启动(Ⅷ)--请求最终目的地 getContainer().getPipeline().getFirst().invoke(request, response)

    当tomcat的Conector保存着StandardService实例,而StandardService保存着Container的实例 当Http11NioProcessor.process()方法 ...