今天早晨,在“软件分析测试交流圈”的微信群里,南京大学的陈教授为2021年的研究生课题研究选择发起了一个“开源测试工具的开发与维护”的话题,引起了热烈的讨论。
其中很多人提到了Fuzzing Test(模糊测试),包括业界的很多专家,都发表了自己对模糊测试的观点,如同济大学的朱少民老师,THU的业界模糊测试领域的专家张超等“大牛”。
专家们认为,Fuzzing Test最近几年发展的特别快,尤其是在学术研究领域。如朱少民老师提到:去年ASE、ISSTA有好几篇论文。张超@THU提到:四大安全会议上,最近几年fuzzing论文占比差不多5%,很热门。
而且,Fuzzing Test在测试应用方面也很落地。如在FuzzingTest工具落地方面很有建树的张超@THU提到:模糊测试是目前最主流的漏洞挖掘方案,80%以上漏洞是fuzzing发现的;近几年fuzzing技术进展很大,测试例生成,调度,变异,监控,进化优化,并行化,硬件辅助,AI辅助等技术都有很多探索。
现在的Fuzzing Test(模糊测试)与早些年的随机测试已经有了很大的不同,所以,赶紧来学习下什么是Fuzzing Test,看看Fuzzing Test是什么神奇技术。
Fuzzing测试的定义
对很多人来说, Fuzzing测试可能比较陌生,甚至没有听说过。
根据维基百科的定义,Fuzzing测试是一种灰盒(或黑盒)的软件测试技术,通过提供大量非预期的输入并监视异常结果来发现软件故障。
Fuzzing的核心思想是自动或半自动生成随机数据或经过变异的数据,输入到一个软件中,以期望触发错误条件或引起程序故障,并通过监视软件是否有异常情况出现,如系统崩溃、断言失败,来发现软件的bug,比如内存泄漏。其中用于输入随机的数据和不合法的数据被称为Fuzz,Fuzzing Test称为模糊测试。
测试人员做测试用例设计时,通常都会根据软件需求,设计一些正向的功能测试用例(正常功能)和异常的反向测试用例(Negaitve用例),然后设计一些非功能的测试用例,如性能测试、安全测试等,对软件进行“全面”的测试。但系统上线后,有时候用户还是会发现一些稀奇古怪的问题。
最著名的例子是苹果手机在锁屏后,用户需要等待128年后才能解锁,而这个bug是一个小孩在玩她妈妈手机时遇到的。因为孩子的思维并不按套路出牌,而测试人员的思维都有定性思维,而这种定性思维是很难打破。

Fuzzing测试更多的是利用程序的随机性进行测试或者接口的调用,打破按套路出牌。
随机测试的典型代表是安卓的Monkey测试(猴子测试),模拟用户在屏幕上随机乱点一通。
Monkey只是模拟用户在界面上随机操作,有些深层次,更底层的问题是很难发现,如协议问题。Fuzzing测试则会构造一些非法的协议包,如蓝牙协议、Wifi协议等来对设备进行攻击,以发现深层次的协议问题。
因此,我认为Fuzzing测试与随机性的猴子测试还是有所不同的,Fuzzing测试在设计用例时,会考虑更多的“逻辑”,是一种有目的性的测试。
在业界,Fuzzing测试常常用于检测软件的安全漏洞。
Fuzzing非常容易找到软件的漏洞,Fuzzing技术特别适合用于发现0 Day漏洞。
目前,Fuzzing技术已经是软件测试、漏洞挖掘领域的最有效的手段之一,也是众多黑客或黑帽子发现软件漏洞的首选技术。
Fuzzing的起源与发展
Fuzz本意是“羽毛、细小的毛发、使模糊、变得模糊”,后来用在软件测试领域,专指模糊测试。

Fuzzing测试不是一种新的测试技术。
Fuzzing技术可以追溯到1950年,当时计算机的数据主要保存在打孔卡片上,计算机程序读取这些卡片的数据进行计算和输出。如果碰到一些垃圾卡片或一些废弃不适配的卡片,对应的计算机程序就可能产生错误和异常甚至崩溃,这样,Bug就产生了。
Fuzzing测试虽然早在1989年就由威斯康星州麦迪逊大学的BartonMiller(巴顿·米勒)教授提出来,但Fuzzing测试的技术突破是最近几年的事。
近年,随着网络应用的普及,人们越来越关注软件的安全性、稳定性和软件质量问题。而软件系统也越来越复杂,经过仔细测试过的软件也会时有bug逃逸。Fuzzing测试作为一种高效的测试方法,被广泛研究。业界的一些测试专家有时也会借助Fuzzing测试,发现一些隐藏比较深的bug。
Fuzzing测试是对其他测试技术的补充。由Fuzzing测试揭露出的问题往往是开发人员不太可能构建的输入触发的。Fuzzing测试扩大了常规自动化测试的代码覆盖范围,通过Fuzzing测试使用的非预期输入通常会触发一些平时不会触发的执行流。
Fuzzing测试具有成本相对较低、发现bug的效率较高、可以自动化执行等优点,是一种有效的软件测试方法。但Fuzzing测试在测试覆盖率等方面仍处于待进一步研究的状态(有很大的研究空间),这大约也是学术界最近一些年热衷于研究Fuzzing测试的一个原因吧。
虽然目前来看,Fuzzing测试的应用范围还比较有限,但随着更多新技术的发展,未来将基于AI的空间搜索和模糊变异算子结合起来,Fuzzing测试的应用范围可能会有更大的扩展。
怎样做Fuzzing测试?
Fuzzing测试一般包括以下6个步骤:

其中,最重要的是生成模糊测试数据(即测试用例),然后不断的执行,发现问题(程序崩溃)。而最关键的是识别目标、识别输入,这其实是需求理解和测试设计的一个过程,是寻找Fuzzing测试方法的过程,只有找到了正确的方法,Fuzzing测试才可能成功。
DigApis在“简单高效的模糊测试——Fuzzing”中,介绍了Fuzzing测试基本的实现方案,如下转发。

1. Fuzzing测试首先需要构造大量的测试用例(初始种子)
进行模糊测试的首要条件就是需要大量的测试用例(即种子)。
例如CharlieMiller对Reader 9.2.0进行的Fuzzing测试,他首先从网上的1515个文件变异得到303.6万个测试用例,然后执行测试,才得到系统Crash的Bug。在对Preview这个软件做测试时,他用了大概279万个测试用例进行测试,才发现Crash。这些数字给我们的直观感受就是需要先构造大量的测试用例,才能保证在Fuzzing测试过程中发现程序的Crash问题。
补充说明下,Charlie Miller是漏洞挖掘领域的大牛,他是第一个成功攻击iPhone、G1 Phone的人,也是蝉联4届的Pwn2Own的冠军。
2. Fuzzing测试最重要的是对测试用例做筛选
实际情况中,并不是说与很多的测试用例,测试就可以发现漏洞。
Fuzzing测试并不是简单的关于生成大量测试用例去做测试的故事,而是关于怎么对测试用例做筛选的故事。
Fuzzing测试不是用几十万、几百万量级的测试用例就可以发现漏洞了。实际上,上面CharlieMiller使用的这几百万个测试用例都是精品,使用这些精品测试用例进行测试,才能发现漏洞。那么,这些精品是怎么筛选出来的,这才是关键,也是进行Fuzzing测试过程中需要做的第二件重要的准备工作。
Charlie Miller在测试PDF的时候,他把网上所有能够下载到的8万个PDF文档都下载下来,然后找到一个最小的子集,这个子集的代码覆盖率和全集的代码覆盖率是一样的。这个最小的子集也就是软件测试中的最初始的集合—1515个文件,在这个最初始的集合上再去做Fuzzing。这就是一个筛选的过程,可以用代码覆盖率作为衡量标准,当然也可以选择其他合适的标准来完成这一筛选过程。
3. 要用正确的方法
Laurent Gaffié说过,他在研究SMB协议的远程调用接口的时候,最先做了很多工作,结果都失败了,直到他将策略改变成了用单字节的网络数据包,才有了大量的产量。所以Fuzzing是要讲方法的,要想清楚可能出问题的是什么地方,你要用什么样的方法去把这个东西找出来。
Charlie Miller也说,很多关于Fuzzing的报告都是讲述如何成功,但是现实中的Fuzzing大部分都是讲关于失败的。可见在现实中做Fuzzing测试的时候,你会遇到很多挫折。所以找到正确的方法非常重要!
4.要用90%的时间阅读文档
做Fuzzing的人,并不是简单的写几行代码,对着软件一通测试就会出来结果。在做Fuzzing之前,需要花很多的时间去阅读文档,理解需求和分析程序。对于复杂的程序,我们要去分析这个程序的功能是什么,它可能出问题的地方在什么位置,会有大量的几乎90%的时间是花在这上面的,这是Charlie Miller和Laurent Gaffié的一个评估。
所以,从测试方法的角度看,Fuzzing测试并不“模糊”,它需要对测试目标、测试对象、测试用例选择等方面都要有清醒的认识。
Fuzzing测试工具
Fuzzing 测试的核心思想是用自动或半自动生成的随机数据输入到软件中进行测试,Fuzzing Test工具基本上可以分为二类:全自动模糊测试工具和半自动模糊测试工具。
全自动模糊测试工具,如bed,就是一个全自动的协议模糊测试工具。bed可对FTP、SMTP、POP、HTTP、IRC、IMAP、PJL、LPD、FINGER、SOCKS4、SOCKS5等多种协议的各种请求,及请求头部进行模糊测试。
半自动模糊测试工具,如Burp Suite、Spike、Sulley等。
bed只能对协议的标准请求、标准头部进行模糊测试,对http的post等自定义的主体的各项没法进行模糊测,而这部份又是我们要测试的主要部分,所以单依靠bed不够。
Burp Suite有一个高度可配置的模糊测试功能Intruder,在Intruder中,只要设置好变量,然后在Payloads中设置好测试用例,即可进行模糊测试。
使用Burp Suite测试时,有时候会觉得受制于工具功能的限制,最好有一个代码式的框架可以自由发挥。Spike就是一个自动化测试的框架。Sulley是模仿Spike用python写的一个框架,用户可以在其上进行二次开发,在Sulley项目上写自己的测试代码,然后保存成py文件、运行py文件执行测试即可。
那么现在最厉害的一个Fuzzing的工具是什么呢?有人推荐AFL,说它是目前最受欢迎的一个工具。这是一个导向型的Fuzzing工具。
Fuzzing通常由盲Fuzzing(Blind Fuzzing)和导向性Fuzzing(Guided Fuzzing)两种。
Blind Fuzzing生成测试数据的时候不考虑数据的质量,通过大量测试数据来概率性地触发漏洞。
Guided Fuzzing则关注测试数据的质量,期望生成更有效的测试数据来触发漏洞的概率,比如,通过测试覆盖率来衡量测试输入的质量,希望生成有更高测试覆盖率的数据,从而提升触发漏洞的概率。
AFL这个工具出来的一个起因就是AFL的开发者认为盲Fuzzing的效率是比较低的;第二个原因是Charlie Miller和Laurent Gaffié所做的样本筛选的方法是有效果的;第三个原因是符号执行,符号执行的理论是非常不错的,但在实际中经常受到可行性、性能等方面的限制。于是在这样一个背景下,AFL出现了。AFL有两个关键词:指令插桩和边覆盖。首先AFL是基于插桩的,能够辅助程序分析;其次AFL是基于边覆盖的,是对Charlie Miller等人基于块覆盖用样本筛选的一个改进和提升。
Fuzzing测试领域还有一些其他的工具,例如,PIT是最新的变异测试系统,可为Java和jvm提供金标准的测试范围,快速可扩展,同时可以集成最新的测试和构建工具。COSTOTest工具可以协助测试人员根据测试意图从应用程序模型中选择服务和组件,检查测试装配的正确性和完整性,满足装配约束,将所需的服务绑定到测试组件库中提供的模拟,检查测试工具的一致性和完整性,生成包括初始化序列的测试组件,启动带有多个测试数据集的测试工具,并收集结果和错误。DLFuzz是首款应用了差分模糊测试的深度学习测试框架,能够引导深度学习系统的行为、暴露其中隐藏的缺陷,DLFuzz采用持续微调的方式,将神经元覆盖率和初始输入和变异输入之间的差异最大化,避免了一系列人工介入。Fuzzing方面的工具还有很多。
随着学术界和工业界对Fuzzing的研究和应用,以及一些新技术的助力,Fuzzing测试工具也会越来越多,功能越来越强大,当然,Fuzzing测试也会越来越不“模糊”。
文章来自,王道质量