#include
int main(int argc,char **argv)
{
char buffer[10];
strcpy(buffer,argv[1])
}
(1)warlock的clfuzz:一个命令行处理器,可用于测试应用程序中的格式字符串和缓冲区溢出漏洞
(2)Adam Greene的iFUZZ。一个命令行模糊器,可用于测试应用程序中的格式字符串和缓冲区溢出漏洞。包括对若干个不同参数的模糊选项,并可根据应用程序的“使用”帮助信息智能地产生模糊测试数据从而对应用程序执行测试。
#include
int main(int argc,char **argv)
{
char buffer[10];
strcpy(buffer,getenv("HOME"));
}
(1)Dave Aitel的Sharefuzz。第一个公开发布的可用的环境变量模糊器,它中途拦截对getenv函数的调用消息并且返回恶意数据。
(2)Adam Greene的iFUZZ。尽管它主要是一个命令行模糊器,iFUZZ包还包括了基本的环境变量模糊能力。iFUZZ使用与Sharefuzz相同的方法,并且比后者在使用过程中更容易被定制。
(1)Michael Sutton的FileFuzz。一个基于Windows图形用户界面(GUI)的文件格式模糊测试工具。
(2)Adam Greene的notSPIKEfile和SPIKEfile。基于UNIX的文件格式模糊测试工具,其中的一个基于SPIKE,另一个则不基于SPIKE。
(3)Cody Pierce的PAIMElfilefuzz。类似于FileFuzz,它是另一个基于Windows GUI的文件
当执行本地化模糊测试时,通常在系统中只有少量的所需要的二进制目标程序。这些程序在执行时具有更高的优先级。在基于UNIX的系统中,这些程序很容易被识别,因为它们包含有setuid或者setgid位集。
setuid和setgid位表明当一个程序运行时,它可以要求提升其特权。对于setuid位,进程将拥有文件的所有者所具有的特权,而不是程序执行者的特权。在setgid位的情况下,进程将拥有文件的组所有者具有的特权。例如,成功的开发一个有setuid根用户和setgid组用户的程序,可能会生成具有这些特权的一个Shell。
使用find命令来构建setuid二进制代码的列表是很简单的,该命令是UNIX和与UNIX类似的操作系统中的一个标准工具。下面的命令可以完整地列出系统中所有的setuid二进制代码。它应当作为根来运行,以防止文件系统的读取错误:
find / -type f -perm -4000 -o -perm -2000
读标志的八进制是4,写标志是2,执行标志八进制是1,,如果一个文件允许用户、组和其他用户进行读和写操作,那么其值为666
第4列代表特殊标志如setuid和setgid位。setuid位被表达为4,而setgid位则表达为2
(1)使用perl简单测试
HOME = `perl -e 'print "X"*10000'` /usr/bin/target
(2)自动化的环境变量测试
如果我们hook掉getenv函数,并且对调用它的所有函数返回字符串,则甚至不需要知道所使用的变量的列表;只需要通过截取所有对getenv的调用来模糊化每个变量。当执行一个快速检查以发现不安全的环境变量使用时,这是很有用的。
我们可以用库预加载的方法hook函数。库预加载本质上是通过使用操作系统链接程序来用用户提供的函数替换函数。
所以我们可以写一个程序
#define BUFFSIZE 20000
char *getenv(char *variable)
{
char buff[BUFFSIZE];
memset(buff,"A",BUFFSIZE);
buff[BUFFSIZE-1] = 0x0;
return buff;
}
sharefuzz工具使用了该方法,该工具被用来发现setuid程序的大量漏洞。为了初始化该模糊测试,只需要将c代码编译到一个共享库中,并使用用户操作系统库的预加载功能
gcc -shared -fPIC -o my_getenv.so my_getenv.c
LD_PRELOAD = ./my_getenv.so /usr/bin/target
(3)如何检测问题
①linux系统中,如果一个应用程序因为一个未处理的信号而终止,那么shell返回代码等于128加上该信号数字。
(1)Argv fuzzer模块。前两个模块是非常类似的,因此可以放在一起来说明。它们将可执行程序的argv[0]和argv[1]的值进行模糊化。这两个模块的基本用法很简单:指定目标应用程序的全路径并使其开始运行。然后它将试图使用不同长度的字符串以及特殊格式的字符串。实际所使用的字符串依赖于模糊字符串数据库,可以用终端用户所提供的简单字符串作为补充。
(2)Single-option/multioption fuzzer(单选/多选模糊器)模块。你可以将此模块认为是一个闭塞的模糊器;也就是说,用户并不向该模块提供有关目标应用程序的任何信息,并且该模块也不关心。它只需要在目标程序中为每一个可能的选项抛出一个字符串值。若使用单选模糊器,iFUZZ将从a循环到Z,并且试图运行下面的命令:
./target -a FUZZSTRING
FUZZSTRING是从iFUZZ内部的模糊测试字符串数据库中取出的一个字符串。这是发现简单的和选项相关的问题的一种快速方法,但是不能发现更加复杂的问题,例如需要多个选项值的那些问题。
(3)getopt模糊器。该模块需要从用户方面得到某些信息。也就是说,它需要知道应用程序通过getopt所使用的选项字符串,这样,它就可以将应用程序将要使用的选项进行模糊化。尽管该模糊化可能是非常耗费时间的,但是它比其他的模糊化类型更加彻底。使用像这样的一个模块,你可能会发现比使用其他的模糊器更加复杂的一些问题。例如,考虑一个应用只有被设置了调试和冗余选项,并且提供了一个长的-f参数时,该应用程序才会触发一个缓冲区溢出。下面是针对此问题编写的一个应用程序的输出,说明了该模块的用法:
$ ./sample_program
Usage:
-f <file Input filename
-o <file> Output filename
-v Verbose output
-d Debug mode
-s Silent mode
基于所输出的使用信息,我们可以确定针对该应用程序的getopt字符串极有可能形如f:o:vds。这意味着选项f和o是作为一个参数,而选项v,d和s只是作为转换。这是如何得知的呢?根据getopt的使用手册:
选项参数是一个字符串,它指定了对应用程序而言是有效的选项字符。该字符串中的一个选项字符的后面可以跟着一个冒号(:’),以指明它获得了所需要的参数。如果一个选项字符的后面跟着两个冒号(‘::’),则表明其参数是可选的;这就是一个GNU扩展。
漏洞程序
int main(int argc,char *argv[])
{
if(argc>1)
printf(argv[1]);
exit(0);
}
使用ifuzz的单选项模糊器完成
(1)OWASP的WebScarab。一个开放源代码的Web应用程序审核套件,具有模糊测试能力。
(2)SPI Dynamics的SPI Fuzzer。一个商业的HTTP和Web应用程序模糊器,它包含在WebInspect漏洞扫描器之内。
(2)Codenomicon的Codenomicon HTTP Test 'Tools。一个商业用HTTP测试套件。
当对请求URl进行模糊测试时,url的每个部分都可以被模糊化:
/dir/page.html?name1=value1&name2=value2
该路径可以被划分为以下的组成部分:
/[path]/[page].[extension]?[name]=[value]&[name]=[value]
当对路径项进行模糊测试时,最常被发现的漏洞就是缓冲区溢出和目录遍历攻击
当研究应用程序时,我们的目标就是要列出下面所有的输入:
(1)lcamtuf的mangleme。第一个公开可用的HTML模糊器。它是一个CGI脚本,可反复发送打乱了的HTML数据到一个浏览器。
(2)H.D. Moore和Aviv Raff的DOM-Hanoi。一个DHTML模糊器。
学将国融
(3)H.D. Moore和Aviv Raff的Hamachi。另一个DHTML模糊器。
(4)H.D. Moore、Aviv Raff、Matt Murphy和Thierry Zoller的CSSDIER。一个CSS模糊器。.David Zimmer的COM Raider。一个易于使用、GUI驱动的COM对象(ActiveX控件)模糊器。
网络协议模糊器可以被分为两个主要的类:以简单协议为测试目标的模糊器和以复杂协议为测试目标的模糊器。下面将分别介绍这两类模糊器的主要特征。
(1)简单协议
简单协议通常只有简单的认证或根本没有认证。它们通常基于可打印的ASCII文本,而不是二进制数据。简单协议不包括长度或校验和字段。此外,典型的简单协议应用程序并不包含很多的状态。
简单协议的一个例子是FTP。在FTP协议中,所有的受控信道中的通信多是以普通ASCII码文本的形式传输的。就认证而言,FTP只需要简单文本形式的用户名和密码认证。
(2)复杂协议
复杂协议典型地要包含二进制数据,偶尔包含人可读的ASCII码字符串。认证可能需要通过加密或某中形式的混绢来实现,其中可能包括多个复杂的状态。
复杂协议的一个例子是Microsoft远程调用(MSRPC)协议。MSRPC是一个二进制协议,在数据传输之前需要执行若干个步骤才能建立起一个通信信道。协议需要长度描述域和分解域。总体上讲,它不是一个很容易实现模糊测试的协议。下面是几个网络协议模糊测试的有用工具:
(1)Aitel的SPIKE。SPIKE是第一个公开发布的模糊测试框架。它包括好几种流行协议的预生成的模糊测试脚本,同时也可以作为一个API来使用。
(2)Michael Eddington的Peach。一个用Python编写的跨平台的模糊测试框架。它非常灵活并且可用来对几乎所有网络目标应用程序进行模糊测试。
现有的网络协议模糊器倾向于采用两种风格。其中一些采用了通用体系结构以能够对不同的协议进行模糊测试。这类工具的代表如SPIKE和ProtoFuzz,我们在第16章网络协议模糊测试:Windows平台上的自动化测试”中从头创建的一个模糊器也是属于这种类型。SPIKE是最广为人知的一个模糊器,在下一章中将对它进行更加详细的介绍。另外一种类型的网络协议模糊器包括哪些被设计为面向特定协议的模糊器。这种类型模糊器的例子包括ircfuzz@,dhcpfuzz以及Infigo FTPStress模糊器。直接从它们的名字你就可以猜出每个所提到的模糊器被设计为面向哪种协议。特定于协议的模糊器通常面向于较小的目标脚本和应用程序,但其体系结构开发的工作量却较大。在第21章“模糊测试的体系结构”中,我们将讨论相对于特定的单机工具而言,创建和使用模糊测试体系结构的价值。下面,让我们来看一些网络协议模糊测试目标的例子。
使用SPIKE
使用ProtoFuzz
优点:
(1)速度。不仅没有网络带宽需求,并且任何位于接收离线数据包和实际解析数据包之间的不重要的代码都可以被删去,从而使得测试性能有所提高。
(2)捷径。有时协议程序使用了定制的加密或压缩算法,或者其中充满了校验和确认代码。内存模糊器能够记录下解压、解密、或校监和确认之后某一-时间点上的内存快照,而不是花费大量的时间来测试所有的过程,减少了测试的工作量。
内存模糊测试的缺点是:
(1)假像。由于原始数据被注入到进程地址空间,不仅没有网络带宽需求,并且任何位于接收离线数据包和实际解析数据包之间的不重要的代码都可以被删去,从而使得测试性能有所改进。
(2)重现。尽管引起一个异常可能会展示出一个可被人利用的漏洞,但是研究者仍然需要在进程之外远程创建异常。这可能会花费不少时间。
(3)复杂。正如我们在第19章“内存模糊测试”和第20章“内存模糊测试:自动化”中将要看到的,这种模糊测试方法的实现极为复杂。
对于基于网络的模糊测试而言,在目标应用中何时发生一个有趣的条件通常是很明显的。在许多情况下,服务器将关闭或者立即崩溃,并且将不能够再连接。而对于文件模糊测试而言,主要是在对客户端应用进行模糊测试时,模糊器将会继续重新开始运行并且销毁目标应用,因此如果不使用适当的监视机制,那么模糊器将可能识别不出一个崩溃情形。这是文件格式模糊测试比网络模糊测试更加复杂的一个应用领域。对于文件格式模糊测试而言,模糊器通常必须要监视目标应用的每次执行以发现异常。这通常可以通过使用一个调试库来动态地监视目标应用中已处理和未处理的异常来实现,同时要将结果记为日志以便于以后的评审。从较高的层次上看,一个典型的文件模糊器的工作过程如下:
1.通过变异或者生成来准备一个测试用例(后者使用的更多)。
2.部署目标应用并指示其加载测试用例。
3.监视目标应用以发现错误,通常是使用一个调试器。
4.当发现一个错误时,记录日志。同样,如果经过一段时间之后没有发现错误,手工销毁目标应用。
5.重复上述过程。
文件格式模糊测试可以通过生成和变异两种方法来实现。尽管这两种方法在实践应用中都很有效,但显然变异或“强有力”的方法更加易于实现。尽管生成方法或“智能强有力”模糊测试的实现要花费更多的时间,但它们可能将发现用其他更原始的强有力方法所不能发现的一些漏洞。
这种模糊测试方法也具有一些缺点。首先,它是一种效率不高的方法,因此需要花费较多的时间来完成对一个单一文件的模糊测试。例如,对于一个基本的word文档的测试。即使是一个空文档,其大小也大约有20KB。每次对一个字节进行模糊化,那么将需要创建并部署20 480个独立的文件。假定每个文件的处理需要2秒钟,那么总共将要花费11个小时,而这只是对一个单一字节值的处理。那么处理其他254个字节将会怎样呢?可以通过使用多线程模糊器在某种程度上回避这个问题,但它却说明了单纯的变异模糊测试具有不高的效率。简化这种模糊测试的另外一种方法是只关注文件中最有可能产生期望结果的那部分,如文件头和字段头。
强有力模糊测试的主要缺点是总是有许多功能将会被遗漏掉,除非你以某种方式收集到包含每个和所有可能特性的一个示例文件集。大多数的文件格式都是很复杂的,并且包含大量的变换。当度量代码覆盖率时,你将会发现将一些示例文件使用于一个应用中并不像另外一种情形那样彻底地执行应用,这另外的情形就是用户真正理解了文件格式,并且手工准备了一些针对文件类型的信息。这个问题可以使用文件模糊测试中的生成方法来解决,也即我们所说的智能强有力模糊测试。
使用智能强制性模糊测试,你必须首先花费一些精力来实际的研究文件规范。一个智能模糊器仍然是一个模糊测试引擎,因此仍然执行一个强制性攻击。然而,它依赖来自于用户的配置文件,以使测试过程更加智能。这些文件通常包含描述文件类型语言的元数据。可以将这些模板想象为数据结构的列表,它们的位置及其可能的取值都是相互关联的。在一个实现的层面上看,这些模板可以用许多不同的格式来表示。
如果选择一个没有任何公开文档的文件格式用于测试,那么作为一个研究者,你必须要在创建一个模板之前对格式化规范进行深入地研究。这可能需要逆向工程的知识,但首先要使用你的好朋友Google来查看一下是否已经有其他人为你做了这项工作。一些Web站点,如Wotsit的Format9,就提供了很好的官方和非官方的文件格式文档服务。一个可选的作为补充的方法是比较文件类型的这些例子以发现一些模式,并且剖析一些被使用的数据类型。记住,一个智能模糊器的有效性直接相关于你对文件格式的理解以及采用一种通用方法将其描述给所使用的模糊器的能力。在第12章“文件格式模糊测试:UNIX平台上的自动化测试”中,当创建SPIKEfile时,我们将展示智能强有力模糊器的一个示例实现。
一旦一个目标应用和测试方法被确定,下→步就是针对所选定的目标应用,研究如何生成适当的输入向量。
将使用到两个模糊器,即notSPIKEfile和SPIKfile,它们分贝实现了基于变异的文件格式模糊测试和基于生成的文件格式模糊测试
使用notSplikefile测试RealPlayer漏洞
export HELIX_PATH = /opt/RealPlayer/
./notSPIKEfile -t 3 -d 1 -m 3 -r 0 -S -s SIGKILL -o FUZZY-sample1.rp
sample1.rp "/opt/RealPlay/realplay.bin %$FILENAME%"
我们使用-t选项告诉工具对每个RealPlayer的激活要延续3秒钟。同时,使用一d选项告诉该工具在它销毁一个空闲进程和启动一个新进程之间要等待1秒钟。使用-m选项来指定部署RealPlayer的3个并发实例,并使用-r选项告诉工具对从0字节开始的整个文件进行模糊测试。我们还使用-S选项来指定字符串模糊测试的模式,并使用-s选项指定SIGKILL信号来终止空闲进程。最后,告诉工具为模糊测试文件名所采取的一种格式,并指定示例文件的文件名sample1.rp,并告诉工具如何执行RealPlayer以使它解析我们的文件。一切准备就绪。notSPIKEfile的输出通过报告一些崩溃信息来表明发现了某些类型的漏洞。
我们将当前目录中的文件列出,可以看到文件FUZZY-sample1.rp-Ox28ab156b-dump.txt被创建。当我们查看该文件时,就可以看到一个生成的描述崩溃时进程状态的详细报告。同时也可以看到导致崩溃的文件的名字。在这个例子中,它被保存为12288-FUZZY-samplel.rp。该文件可以用来再现崩溃。
使用FileFuzz来完成
FileFuzz的开发经历了3个不同的阶段。首先,它创建将要被模糊化的文件。它通过获得-个合法的用户提供的文件并基于所提供的目录,来进行有计划的连续变异,并保存结果文件的方法来实现此功能。其次,变异后的文件一个接一个的被部署到目标应用中。这些文件被重复部署,并且结果进程最终基于用户定义的一个超时而被销毁。最后,内嵌的调试功能监视进程以识别可能发生的已处理和未处理异常。当这些事件被识别时,它们被记录下来并报告给终端用户。下面对每个阶段进行更加详细的介绍。
(1)创建文件
可以看到,FileFuzz可以处理二进制文件格式和ASCII文本文件格式。在二进制文件中,采用了两个单独的方法,名为宽度和深度。为了区分宽度和深度,我们将使用钻井取油来进行类比。如果你正在一个广阔的区域内寻找石油,那么你不可能随意就开始钻井。你必须要首先使用一些不同的技术来确定那些最有可能包含石油的位置。可能你会研究地图,分析岩石构造或者使用地面探测雷达。不管使用什么方法,一旦你发现这些感兴趣的位置,你就可以进行试探性的钻探以发现最佳的位置。
对于文件格式模糊测试,我们也采用了一种类似的方法。首先,我们使用宽度方法来发现感兴趣的位置,然后使用深度方法来确定是否找到了需要的内容。宽度方法意味着覆盖文件中的所有字节或者一个特定的区域。通过连续的将区域内的字节值改变为一个预定义的值来生成单独的文件,直到整个区域都被覆盖。一旦该操作完成,这些文件被一个接一个的部署以确定这些变化是否导致了任何异常。
有时候我们会比较幸运,不再需要进行其他操作。有些时候,异常结果是很有趣的,并且终端用户可以控制崩溃,因为被引入的变异值在寄存器中是显而易见的。但在大多数情况下,并不会这么简单。一个崩溃将会发生并且位置可能是有趣的,但如果终端用户拥有控制崩溃的任何方法,那么寄存器的值将不是清晰可见的。在这种情况下,就该转向使用深度方法了。一旦感兴趣的字节位置在文件中被识别,我们将关注这些位置并使用深度方法来为该位置试验所有可能的字节值。当我们查看所生成的崩溃时,就可以了解能够控制该崩溃的程度。如果不论提供什么字节值,异常发生的位置和寄存器值都保持一致的话,那么就表明没有对该异常的控制权。然而,如果崩溃发生在不同的位置或寄存器的值连续地改变,那么很显然我们对所产生的异常至少造成了一些影响,而这种影响基于对文件进行变异时所使用的值。
FileFuzz也可以处理ASCII文本文件。它是采用如下的方法来进行处理的,即允许终端用户首先选择一个标识将要被覆盖位置的字符串,然后请求三个单独的输入来确定用来变异文件的输入。终端用户必须提供一个字符串值及其长度,以及它将要被增加的次数。下面来看一个例子。在通常情况下,ASCII文本文件,如*.ini,都包含如下格式的名-值对:
name = vlue
典型的,我们想要将值覆盖。假定我们要用连续的10个A字符来覆盖值。在这种情况下,我们首先将所发现的值设置为=字符,它标识了值的开始。然后将替换值设置为AX10X10。这将生成10个变异文件,即当一个=字符被发现时为每个实例使用覆盖值。所生成的10个文件将在值的位置包含从10到100个A字符。
(2)应用程序执行
一旦模糊文件被创建,我们需要将它们部署到应用程序中。例如,如果我们已经将*.doc文件变异,想要将它们部署到微软的Word程序中。FileFuzz使用来自于Windows API的createProcess()函数来实现此功能,因此我们可以使用FileFuzz来部署应用程序,只要能够确定相同的应用程序如何从命令行部署,并且包含可能传递给应用程序的必要标志。我们将在本章后面详细介绍如何识别这些信息。
在文件格式模糊测试中,我们需要将同一个应用程序部署成百上千次。如果我们让所有前面已经执行过的进程开始运行,那么将会很快用完可用内存。因此,执行阶段并没有完成,直到在一个预定义的时间间隔之后该进程也被销毁为止。我们允许终端用户对此进行控制,方法是通过在执行标签下包含一个毫秒字段,以标识在需要时被强制销毁之前进程所允许运行的总次数。
(3)异常检测
对于大多数类型的模糊测试而言,识别异常的最好方法是使用一个调试器。调试器的优点是它既可以识别出已处理的异常,也能够识别出未处理的异常。Windows具有强大的异常处理功能,并且通常可以从它所读取的模糊文件中进行恢复。然而,识别这些异常情形是非常重要的,因为在文件异常处的一个小的改变就可以结束创建一个不可恢复或可利用的条件。
调试器在文件格式模糊测试过程中的使用,并不像它在其他类型模糊测试中那样直接。在这种情况下,我们无法手工将一个调试器关联到目标应用程序并使模糊器运行。这是因为模糊器要经常部署并销毁目标应用程序。因此,它同样也会销毁调试器。为此,我们需要利用一个调试API并且在模糊器中直接创建调试器。采用这种方法,模糊器就可以执行目标应用,并且自己关联到一个调试器,然后销毁目标应用。因此,模糊器在每次目标应用被部署时来监视异常。
对于FileFuzz而言,我们创建了crash.exe,它实际上是一个被GUI应用执行然后部署目标应用的单机版调试器。它是一个完全的单机命令行应用程序,同时由于FileFuzz是一个开源项目,因此你可以在你自己的模糊测试项目中免费使用crash.exe。