honggfuzz漏洞挖掘技术深究系列(2)—— Persistent Fuzzing

上篇《honggfuzz漏洞挖掘技术深究系列(1)——反馈驱动(Feedback-Driven)》讲到基于软件的代码覆盖率驱动fuzzing的方式,除了软件还有硬件方式,即基于Intel BTS (Branch Trace Store) 或Intel PT (Processor Tracing) 去计算代码覆盖率,同时要求Linux内核>=4.2,这种方式的最大好处是完全由硬件支配,无所谓软件是闭源还是开源。由于硬件环境受限,我也一直未使用过,有此条件的同学可以试下。

本篇主要讲下持久型fuzzing(Persistent Fuzzing),即fuzzing API,这种方式会更精准和高效的。

先看使用方法:

1
2
3
4
5
6
7
8
9
10
11
12
$ cat test.c
#include <inttypes.h>
#include <testlib.h>
extern int LLVMFuzzerTestOneInput(uint8_t **buf, size_t *len);

int LLVMFuzzerTestOneInput(uint8_t *buf, size_t len) {
_FuncFromFuzzedLib_(buf, len); // 目标函数
return 0;
}
$ clang-4.0 -fsanitize-coverage=trace-pc,indirect-calls,trace-cmp fuzzedlib.c -o fuzzedlib.o
$ clang-4.0 test.c fuzzedlib.o honggfuzz/libhfuzz/libhfuzz.a -o test
$ honggfuzz -z -P -f INPUT.corpus -- ./test

这里用到几个编译选项:

  • trace-pc:追踪执行过的基本块BB,在每个edge中插入__saitizer_cov_trace_pc()函数,可定义该函数作为相应的回调处理
  • indirect-calls:在每个间接调用中添加PC追踪,与前面的trace-pc或trace-pc-guard联合使用,回调函数:__sanitizer_cov_trace_pc_indir
  • trace-cmp:追踪每个比较指令和swith语句

以trace-pc为例,测试代码如下:


用trace_pc编译:

可以看到自定义的函数被执行,输出执行过的不同pc地址,其它编译选项的用法同上。
下面是honggfuzz对各个回调函数的定义情况:

然后就是记录代码覆盖率情况并进行统计,跟驱动反馈的方式一样了。

再回头看使用示例中的LLVMFuzzerTestOneInput函数,honggfuzz是如何处理它的呢?

通过for无限循环调用目标函数进行Fuzzing,其中参数buf,即样本文件内容,len是数据长度。

最后根据发现的新路径,将相应的样本作为新样本继续fuzzing。