模糊测试(Fuzzing),是一种通过向目标系统提供非预期的输入并监视异常结果来发现软件漏洞的方法。其核心思想是自动或半自动的生成随机数据输入到一个程序中,并监控目标程序异常,如崩溃,断言(assertion)失败,以发现可能的程序错误,比如内存泄漏等。
模糊测试是一项简单的技术,它用随机坏数据(也称做 fuzz)攻击一个程序,然后等着观察哪里遭到了破坏。但这一过程却能揭示出程序中的重要 bug。它是一种介于完全的手工渗透测试与完全的自动化测试之间的安全性测试类型。它充分利用了机器能够随机生成和发送数据的能力, 同时,也尝试将安全专家在安全性方面的经验引入进来。
模糊测试的实现过程非常简单,主要包括以下几个步骤:
常用的模糊测试框架包括以下几种:
antiparser 该框架以Python语言编写,是一个专门帮助模糊测试器创建随机数据的API。该工具可以跨平台,仅仅要求有python解释器就行。
Dfuz 该框架是Diego Bauche用C开发的,经常更新。该框架已经发现了很多漏洞。Dfuz是开源的,可以下载。但是该框架的源代码采用了一种严格的开原许可,未得到作者的允许不可以使用复制该框架的源代码。
SPIKE 最广泛使用最知名的一个框架。使用C语言编写,提供了一系列允许快速和高效的开发网络协议模糊测试器的API。在SPIKE中,数据结构被分解表示成块,也叫SPIKE,这个块同时包含二进制数据和块大小。
Peach python编写的,是一个开源的框架。Peach体系结构允许研究者聚焦于一个个的特定的协议的子组件,然后组合起来创建完整的模糊测试器。这种方法可能不如基于块的开发速度,但是对代码的复用的支持比其他模糊测试工具好。
通用目的模糊测试器(GPF) GPF可以产生无数个测试,无数个变异。(其他的根据规则不会是无数个),该框架主要的有点是可以用很低的成本建立并运行一个模糊测试器,通过GPF的多种模式对外提供功能。
Autodafe 这个框架可以简单的描述成下一带的SPIKE,该框架能够对网络协议和文件格式进行模糊测试。他最吸引人的就是调试组件。
由于前段时间使用SPIKE框架做了一些网络协议测试的工作,所以这里主要记录下学习过程。
网络协议模糊器的测试对象主要是各类网络产品中的网络协议解析模块,目的是测试其在组装、解析网络协议过程中是否存在漏洞。其思想是模糊器通过Socket 与被测目标之间进行通信,向被测目标应用发送变异或包含错误的模糊值, 并监视目标应用以发现错误。使用网络协议模糊器进行模糊测试, 需要研究各类协议的规范和标准,以便创建合理的测试数据。
目前 ,最常见的网络协议模糊测试实施方案有两种:
从技术层面上讲,SPIKE是一个模糊测试创建工具集,它允许用户用C语言基于网络协议生成他们自己的测试数据。SPIKE定义了一些原始函数,这允许C程序员可以构建fuzzing数据向目标服务器发送,以期引起产生错误。
SPIKE有块(blocks)的概念,这可以在SPIKE内部推测出指定部分的大小。产生的数据就可以被SPIKE以不同的格式嵌入到自身测试数据中去。当需要在特定位置嵌入精确数据的时候,blocks是非常给力的,它大大节省了我们自行计算的时间。
SPIKE用模糊字符串库中的内容迭代模糊变量,达成模糊测试。模糊字符串可以是任何数据类型,甚至是XDR编码的二进制数据数组。SPIKE是一个GPL的API和一套工具,它使你可以快速创建任何网络协议压力测试的测试器。大多数协议都是围绕着非常类似的数据格式化建立的。这些协议中的许多都已经在SPIKE中得到支持。
SPIKE使用C语言编写,运行平台UNIX,它包含一部分预先写好的针对具体协议的模糊测试器,以下是这些模糊测试器的列表:
SPIKE有几个通用模糊测试器,他们接收脚本作为输入,下面列出能在SPIKE中找到的通用模糊测试器:
接下来介绍一下字符串、二进制数据、块以及其他有用的结构。
s_string("string");//作为“SPIKE”的一部分,简单的输出字符“string”
s_string_repeat("string",200);// 重复字符“string”200次
s_string_variable("string");//向你的SPKIE中插入一段字符串,字符串"string"将被用作这个变量的第一次循环,以及其他循环中的s_string_variables变量。
s_binary("\x41");//插入二进制数据,16进制表示为0x41,ASCII码为A
s_binary_repeat("\x41", 200);//插入二进制数据0x14 200次
对于SPIKE的二进制命令,相同数据不同书写方式都是可用的。为了输出跟上面相同的16进制字符,我们可以用“41”或者“0x41”,我们还可以混合这些值(如:用“410x41\x42”来输出ASCII码“AAB”)。任何空格都将被忽略,所有这些方便剪切/粘贴的数据都来自于这些用16进制表示的应用程序,如包捕获工具/调试器等。
s_block_start("block1"); //定义块"block1"的起始位置
s_block_end("block1"); // 定义块"block1″的结束位置
s_blocksize_string("block1", 2); // 添加一个2个字符长的字符串到SPIKE中来代表block1块的大小
s_binary_block_size_byte("block1"); //添加1byte数据到SPIKE中来代表block1块的大小
这是向SPIKE添加块数据多种方法中的两个例子。当然还有其他方法允许你用各种格式的块,甚至一些允许增加块预设值。
s_read_packet(); // 读取来自服务器的数据,并显示到屏幕上
s_readline(); // 从服务器上读取一行输入信息
你也可以参考SPIKE内其他脚本,用C语言添加具有额外功能的脚本。一个特别有用的函数是printf(),他可以通过终端显示,给我们提供更多信息。
要想看一下其他选项,在src目录下的spike.h文件里执行一下grep显示,筛选“block_size”或“blocksize”即可。
最后简单介绍一下使用generic_send_tcp函数的例子
在基于TCP的应用服务程序下,我们可以通过用脚本解释器generic_send_tcp来执行.spk脚本文件的方式,发送特定的数据到特定的IP地址、TCP端口。当然,还有一个generic_send_udp脚本解释器,它的作用类似,就是通过UDP的方式发送数据。
解释器generic_send_tcp将被用来fuzz我们的应用程序,不带参数的运行结果如下:
./generic_send_tcp
argc=1
Usage: ./generic_send_tcp host port spike_script SKIPVAR SKIPSTR
./generic_send_tcp 192.168.1.100 701 something.spk 0 0
前三个参数很明确,分别定义了fuzzing主机的主机名、端口号以及SPIKE脚本的名字。在SPIKE脚本中,“s_string_variables”是用来向fuzzed数据插入字符串的命令。若是用了一个以上的 “s_string_variables”参数,你可以跳过使用早起的“s_string_variables”为SKIPVAR设置为适当的值。例如,你的脚本中包含三个“s_string_variables”参数,并且你想忽略前两个参数,直接fuzz第三个参数,我们就可以设置 SKIPVAR 为2(变量的编号跟数组一样,从0开始)。每一个“s_string_variables”也有一些列内置的模糊测试字符串值。如果你想跳过前10个字符,想从第11个字符开始fuzzing,你就可以设置SKIPSTR参数为10(同样从0开始计起)。
当你使用generic_send_tcp时,它输出变量、字符串到测试中的命令行。因此如果你要恢复被打断的SPIKE会话,你可以用得着SKIPVAR、SKIPSTR这两个命令行参数参数。
参考资料:
1. 模糊测试百度百科
2. 网络协议模糊测试
3. 模糊测试–强制性安全漏洞发掘
4. 云端模糊测试挖洞实例
5. 关于Fuzz工具的那些事儿
6. 模糊测试框架