摘要:生成的崩溃测试用例可能很难分析,因为模糊测试的行为不能告诉你软件的内部操作。模糊测试向软件系统提供随机输入。
软件质量保证
专注于测试圈,自动化测试,测试平台开发,测试新技术,共享大厂测试场地,可以帮助推广BATJ和其他大厂!欢迎添加VX交流:ISTE1024
测试同行或多或少听说过模糊测试,但不知道它是什么。本文将详细介绍Fuzzing Test,帮助你快速理解。
什么是“模糊测试”?
Fuzzing是一种发现软件缺陷的方法,它向程序提供随机输入,以找到导致程序崩溃的测试场景(原理有点类似于猴子测试)。可以帮助您快速了解程序的整体健壮性,并帮助您发现和修复关键缺陷。
它是一种黑盒测试技术,不需要访问源代码,但仍然可以用来测试有源代码的软件。这是因为它可以更快地发现缺陷,减少大量代码审查的成本。
模糊测试的优缺点Fuzzing在某些业务中非常有用,但它毕竟不是银弹。以下是模糊技术的一些优点和缺点。
优势
可以说是不费吹灰之力就能得到结果。--一旦fuzzer启动并运行,它可以停留几个小时、几天或几个月,无需交互即可找到错误。
可以发现人工审计中遗漏的错误。
可以提供目标软件健壮性的总体概述。
劣势
不穷尽所有bug --模糊测试可能会遗漏不触发整个程序崩溃的bug,很难覆盖只在非常特殊的情况下触发的bug。
由此产生的崩溃测试案例可能很难分析,因为模糊测试的行为不能告诉你任何关于软件内部操作的事情。
具有复杂输入的程序可能需要更多的工作来产生足够智能的模糊测试器,以获得足够的代码覆盖率。
聪明/愚蠢模糊测试
模糊器向软件系统提供随机输入。内容可以是网络协议、某种格式的文件或用户可以直接输入的数据。输入法是完全随机的,我们不知道预期的输入应该是什么样子,或者经过一些修改后看起来可能是有效的输入。
产生完全随机输入的引信被称为哑引信。少量的工作可以用很小的代价产生--的结果,这是模糊测试的一大优势。但是,有时候一个程序只会在特定的场景下进行一定的处理。例如,一个程序的输入需要被传递到“name”字段中,这个字段有一个与之相关联的“name length”。
如果这些字段的格式不够有效,程序无法识别,它可能永远也不会读取名称。如果这些字段以有效的形式存在,但是长度值被设置为不正确的值,程序可能会从包含名称的缓冲区中读取并导致崩溃。没有有效的投入,这是不可能的。在这些情况下,您可以使用智能模糊器,它是基于输入的特定规则实现的,如协议定义或文件格式规则。它可以构造大多数有效的输入,并且只混淆基本格式内的输入。
模糊器的类型
从广义上讲,Fuzzer可以分为两类:基于--突变的和基于世代的。
基于变异的模糊器
基于变异的Fuzzer可以说是最容易创建的类型之一。该技术适用于哑引信,但也可用于智能引信。通过变异,有效输入样本被随机变异产生异常输入。
哑突变模糊器可以简单地选择一个有效的输入样本,并随机改变它的一部分。因为输入通常仍然与有效输入足够相似,这意味着无需进一步的智能处理就可以实现良好的代码覆盖率。
以下是基于突变的模糊器可以使用的两种技术。
交通回放
Fuzzer可以获取保存的样本输入,并在变异后再次播放。这对于文件格式的模糊处理非常有效,可以保存一些样本文件,并进行模糊处理以提供给目标程序。
简单或无状态的网络协议也可以通过重放进行有效的混淆,因为Fuzzer不需要进行大量的合法请求就可以深入协议。对于更复杂的协议,回放可能更困难。这是因为Fuzzer需要以动态方式响应程序,以允许处理继续进入协议。
代理
你可能听说过中间人(MITM)是渗透测试人员和黑客使用的技术,但它也可以用于基于突变的网络协议的模糊测试。通过MITM,您处于客户端和服务器之间,拦截并可能修改它们之间传输的信息。这样,你就像是介于两者之间的代理人。
通过将Fuzzer设置为代理,它可以根据您对服务器或客户端的模糊处理来更改请求或响应。同样,Fuzzer可以随机更改一些请求,或者智能地将请求锁定在您感兴趣的特定协议级别。基于代理的模糊测试允许您使用现有的网络程序部署架构来快速插入模糊测试层,而无需让您的模糊器像客户端或服务器本身一样工作。
基于世代的模糊器
基于生成的Fuzzer实际上是从零开始生成输入,而不是改变现有的输入。他们通常需要一定程度的智能来构造至少对程序有意义的输入,尽管生成完全随机的数据也是技术上生成的。
生成一个模糊器通常是将协议或文件格式分成几个块,这些块可以按照有效的顺序建立,其中一些块是独立随机模糊的。这可以创建保留其整体结构的输入,但它也包含不一致的数据。这些块的粒度和构建这些块的智能决定了Fuzzer的智能水平。虽然基于突变的模糊处理可以产生与基于生成的模糊处理类似的效果(因为突变会随着时间随机应用,而不会完全破坏输入的结构),但输入的生成可以确保这一点。
生成模糊器也可以更容易地深入协议,因为它可以构造有效的输入序列,并模糊通信的特定部分。它还允许Fuzzer作为一个真正的客户端/服务器,生成正确的动态响应,但这些响应不能盲目重放。
进化模糊器
进化模糊测试是一种先进的技术。它允许Fuzzer使用来自每个测试用例的反馈来理解输入格式。例如,通过测量每个测试用例的代码覆盖率,Fuzzer可以计算出测试用例的哪些属性可以锻炼给定的代码区域,并逐渐演化出一组覆盖大部分程序代码的测试用例。进化模糊测试通常依赖于类似于遗传算法的其他技术,并且可能需要某种形式的二进制工具来正确操作。
模糊测试测量什么?
即使这是一个相对于dumb的模糊测试,也要记住你的测试用例实际上可能会碰到代码的哪一部分。举个简单的例子,如果您正在探索一个使用TCP/IP的应用程序协议,并且您的模糊器随机改变了对原始数据包的捕获,那么您很可能会破坏TCP/IP数据包本身。因此,应用程序不可能处理您的输入。此外,如果您正在测试一个将文本图像解析为真实文本的OCR程序,但是您正在改变整个图像文件,那么您可能会比实际的OCR代码更频繁地测试它的图像解析代码。如果您希望专门进行这种OCR处理,您可能希望保持图像文件的标题有效。
模糊操作过程
为了有效运行,Fuzzer需要执行以下重要任务:
生成测试用例
记录测试或重现案例所需的任何信息。
提供一个测试用例作为目标程序接口的输入。
检测崩溃
Fuzzer通常将这些任务分成独立的模块。例如,一个库可以根据定义突变数据或生成数据,另一个库可以向目标程序提供测试用例。
生成测试用例
测试用例的生成将取决于是否采用基于变异或生成的模糊处理。无论采用哪种方法,都会有需要随机变换的东西,不管是特定类型的字段还是任意的数据块。
这些转换可以是完全随机的,但值得注意的是,边界和极端情况往往是程序中错误的来源。所以,你可能要偏向这种情况。
很长很长的字符串或Null。
可以支持的最大值和最小值的整数。
像-1,0,1和2这样的值。
根据您想要混淆的内容,可能有一些特定的值或字符更容易触发错误。例如:
空
分号
格式化字符串值(%n,%s等。)
应用特定关键字
可重复性
重现测试用例最简单的方法是记录检测到崩溃时使用的确切输入。在某些情况下,还有其他方法来实现再现性。一种方法是存储用于测试用例生成的随机部分的初始种子,并确保所有后续的随机行为遵循可以追溯到该种子的路径。通过使用相同的种子重新运行Fuzzer,该行为应该是可重复的。例如,您可以只记录测试用例编号和初始种子,然后使用种子快速地重新执行生成,直到到达给定的测试用例。
当目标程序基于过去的输入积累了依赖关系时,这种技术很有用。先前的输入可能导致程序初始化其存储器中的各种项目,这是触发错误所必需的。在这些情况下,仅仅记录崩溃的测试用例不足以重现错误。
与目标程序的接口
连接目标程序以提供模糊输入通常很简单。对于网络协议,它可能涉及在网络上发送测试用例或响应客户的请求。对于文件格式,这可能意味着用指向测试用例的命令行参数来执行程序。然而,有时提供的输入形式不容易以自动化的方式生成,或者编写程序脚本来执行每个测试用例是昂贵的,并且被证明是非常慢的。在这些情况下,创造性思维可以找到用正确数据训练相关代码片段的方法。
例如,这可以通过在内存中手动设置一个程序来执行解析功能来实现,而输入参数完全在内存中。这可以消除程序在每个测试用例之前通过冗长的加载器的需要。此外,通过使测试用例完全在内存中生成和提供,而不是通过硬盘驱动器,可以进一步提高速度。
碰撞检测
碰撞检测是模糊测试的关键。如果您不能确定程序崩溃的确切时间,您就不能确定测试用例是否触发了错误。
附加一个调试器。
这可以为您提供最准确的结果,并且您可以编写一个调试器脚本,以便在检测到崩溃时为您提供崩溃跟踪。但是,附加调试器会大大降低程序的速度,并造成相当大的开销。在给定的时间内,你能产生的测试用例越少,你发现崩溃的机会就越小。
查看目标进程是否已经消失
不需要附加一个调试器,你可以简单地看到在执行测试用例之后,目标的进程ID是否仍然存在于系统中。如果进程消失了,它可能已经崩溃了。如果您想了解更多关于崩溃的信息,您可以稍后使用调试器重新运行测试用例。你甚至可以在每次崩溃时自动这样做,同时可以避免在每种情况下连接调试器所导致的速度变慢。
超越时间
如果程序对你的测试用例有一个正常的响应,你可以设置一个超时,在此之后你认为程序已经崩溃了。这也可以检测导致程序无响应但不一定终止的错误。
无论您使用哪种方法,只要程序崩溃或变得没有响应,您就应该重新启动它,以便模糊测试可以继续。
模糊测试质量
您可以做一些事情来衡量或提高您的模糊测试的质量。虽然这些都是需要记住的有用的东西,但是如果你在某段时间内有很多独特的崩溃,你可能就不需要再为这些事情而烦恼了。
速度
速度可能是模糊测试中最重要的因素之一。每秒/分钟可以运行多少测试用例?当然,合理的值取决于目标,但是您可以执行的测试用例越多,您就越有可能在给定的时间段内找到崩溃。模糊测试是随机的,所以每个测试用例就像一张彩票。你应该尽可能多地得到它们。
您可以做许多事情来提高测试用例的速度,例如提高生成或变更例程的效率,并行化测试用例,减少超时,或者在不显示图形用户界面的情况下以无头模式运行程序。如果你愿意,你可以简单地购买一个更快的包。
分类崩溃
找到坠机地点只是整个过程的开始。一旦你发现一个崩溃的测试用例,你需要分析它,找出错误,并根据你的动机修复它或为它写一个bug。如果您有数千个崩溃的测试用例,这可能是相当令人生畏的。通过对崩溃进行分类,您可以根据您最感兴趣的崩溃对其进行优先级排序。这还可以帮助您识别一个测试用例何时触发了与另一个测试用例相同的错误,因此您只保留与独特的崩溃相关的用例。
为了做到这一点,您需要一些关于崩溃的自动信息,以便您可以做出决定。在目标机器上运行测试用例并将它们连接到调试器可以提供崩溃跟踪,并且您可以分析它们以找到诸如异常类型、寄存器值、堆栈内容等内容。
减少测试用例
因为模糊测试随机改变输入,一个崩溃的测试用例通常有多个与触发错误无关的改变。测试用例缩减是将测试用例缩减到触发bug所需的有效输入的最小变更集,所以在分析中只需要关注这部分输入。
这种减少可以手动完成,但也可以由Fuzzer自动完成。当遇到崩溃的测试用例时,Fuzzer可以多次重新执行测试用例。每次,它逐渐减少对输入的更改,直到保留最小的更改集,同时仍然触发错误。这可以简化您的分析,并帮助分类崩溃的测试用例,因为您将确切地知道输入的哪些部分受到了影响。
代码覆盖率
这是一个衡量一个程序有多少代码被Fuzzer执行的标准。原则是您获得的覆盖率越多,您实际测试的程序就越多。测量代码覆盖率可能很棘手,通常需要二进制工具来跟踪代码的哪些部分正在被执行。你也可以用不同的方法来测量代码覆盖率,比如按行、按基本块、按分支或者按代码路径。
代码覆盖率对于模糊测试来说并不是一个完美的衡量标准,因为有可能在没有发现漏洞的情况下执行代码。而且,经常会有一些代码区域几乎不会被执行,比如安全错误检查,反正我们不太可能真的需要或者感兴趣。尽管如此,某种形式的代码覆盖率测量可以让我们知道你的Fuzzer在程序中触发了什么。特别是当你的模糊测试完全是黑盒的时候,你可能对程序的内部运行不是很了解。一些有助于代码覆盖率的工具和技术包括Pai Mei、Valgrind、DynamoRIO和DTrace。
模糊测试框架
目前,市场上有许多框架允许您创建Fuzzer,而不必从头开始构建。这些框架列举如下:
拉达姆萨
Radamsa的设计是易于使用和灵活的。它试图对各种输入类型做“公平的工作”,并包含一些不同的变异模糊算法。
苏利
Sulley提供了一个全面的生成框架,允许将结构化数据表示为基于生成的模糊处理。它还包含帮助记录测试案例和检测崩溃的组件。
桃子
Peach框架可用于文件格式和网络协议的智能模糊测试。它可以基于生成和变异执行模糊测试,并包含帮助建立模型和监控目标的组件。
长钉
SPIKE是一个网络协议模糊器。它要求用户熟悉C语言,并且被设计为在Linux上运行。
磨工
Grinder是一个网页浏览器Fuzzer,也有帮助管理大量崩溃的功能。
节点模糊
NodeFuzz是一个基于node.js的web浏览器harness,包含了仪器模块,可以从客户端获取更多的信息。
澳大利亚橄榄球联盟
AFL是一个灰箱模糊测试工具,它使用在目标代码中编译的工具。AFL最初是为Linux中的C和C++程序编写的,后来分叉为支持Windows、Java和。网。
先前的建议
接口测试框架4的开发实践:HTTP方法封装
接口测试框架开发实践3:用例管理模块
经验分享|测试工程师的转型测试开发流程
接口测试框架开发实践5:配置文件读取
接口测试框架开发实践2:接口自动化测试框架的设计思路
接口自动化测试框架实践1:接口测试概述
Pytest系列(7)-黑盒测试DDT
评论前必须登录!
注册