笔者今天下午进行了一场二进制研究相关面试,但面试后发现笔者在fuzz方面的原理和概念上并不熟练,并且在深度上严重不足,于是总结归纳,希望系统性的学习以下模糊测试,并决定专开一栏,进行相关的系统深入学习。

了解模糊测试

模糊测试概念

通过自动化生成并执行大量测试用例来返现目标的未知漏洞

模糊测试原理模型

生成随机字符串

生成随机字符串是一个比较简陋的方法,可能效果并不好,尤其是协议部分,可能在协议部分就出现问题

image-20250609182518403.png

基于模板生成

这里我们以HTTP协议为例,以下是一个HTTP协议的例子

1
2
3
4
http 1.0
GET / HTTP/1.1
Host: developer.mozilla.org
Accept-Language: zh

其中我们知道,类似于http这一类的信息是不变的,而像1.0、GET类似的信息是可变的,而下方Host中的操作是资源字符部分。基于以上我们的分析,我们可以生成一个模板,然后根据模板来进行字符串的生成,只有通过这一模板生成的字符串才是有意义的。

image-20250609184102524.png

基于覆盖式的生成(覆盖制导)

我们假设有以下的代码

1
2
3
4
5
6
7
8
9
10
if (data[0] > 0){

func1()

}
else{

func2()

}

覆盖引导后就会将代码的流程生成出来程序的流程图,这个流程我们将其称为 代码路径

image-20250609185052069.png

实现原理

那么这样的功能是怎么实现出来的呢?这里就有一定插桩相关的原理,我们再回到上面的代码来看,我们可以在程序中插入log1,log2,示例如下

1
2
3
4
5
6
7
8
9
10
11
12
log(__main_start)
if (data[0] > 0){

func1()
log(func1.info)
}
else{

func2()
log(func1.info)

}

此后程序可以对生成的log文件进行跟踪,通过生成的log来分析运行的路径来确定程序的运行流程,从而生成流程图。

为什么要实现覆盖引导

因为每次进行代码路径分析的时候,我们会检测到新的代码路径,而不断的扫描新路径则会不断提高代码的覆盖率,并说明这段代码是有意义的,那么接下来需要将代码放入后续的生成中,基于这个数据上进一步变异生成,然后不断重复这一个过程。

特殊条件的fuzz

图形化FUZZ

很多时候的程序并不像我们我们上述所给出的程序一样,一定存在输入和输出的流,那么此时我们要怎么进行fuzz呢?我们知道,大多数的UI实现都是会对UI和func存在绑定的,那么我们的第一部程序拆解,最终拆解成我们可以进行调用的某一个函数,那么这样后我们就可以和常规的fuzz一致了。

持久化程序fuzz

我们标准的fuzz的理想情况是程序是运行后给出结果立刻结束,第二次测试则进行一个新的运行。但是很多程序并不会这样,譬如webserver,它会一直停留运行,那么我们需要有以下的方法

  • idapatch将程序修改程序逻辑为只处理1次
  • 修改fuzz逻辑
    • 自己实现fuzz
    • 持续性fuzz

经典模糊测试工具

  • AFL
  • libafl (iot常用,rust静态连接低依赖,有点像乐高,可以自己拼接功能)
  • winafl(window版)
  • syzkaller(同原理,进程fuzz)
  • libfuzzer(可以针对程序的某个函数)
  • fuzzilli(CS引擎fuzz)