SPIKE介绍
SPIKE——模糊生成工具
理论:
l SPIKE是一个GPL的API和一套工具,它使你可以快速创建任何网络协议压力测试的测试器。
l 大多数协议都是围绕着非常类似的数据格式化建立的。
l 这些协议中的许多都已经在SPIKE中得到支持
l 其他的协议也很快会得到支持
SPIKE的目标:
l 通过下面的方法找到新的漏洞
u 使快速重新产生一个复杂的二进制协议变得非常简单。
u 在SPIKE中开发一个对类似协议产生影响的不同类型漏洞的知识库。
u 在新的程序中测试老的漏洞
u 使得手工干预协议变得简单。
SPIKE API的工作方式:
l 独特的SPIKE数据结构支持长度和分块
u s_block_start(),s_block_end(),s_blocksize_halfword_bigendian();
l SPIKE实用例程使得处理二进制数据,网络代码和常用的编组例程变得简单
u S_xdr_string()
l SPIKE模糊框架自动重复所有可能有潜在问题的地方
u s_string(“Host: “);s_tring_vayiable(“localhost”);
SPIKE的数据结构:
l SPIKE是一种先进先出队列,或称为“Buffer Class”
l SPIKE能够自动填充长度域:
u S_size_string(“post”,5);
u S_block_start(“Post”);
u S_string_variable(“user=bob”);
u S_block_end(“post”);
长度域:
l 长度域可以有很多变化
u word/ halfword/ string
u big endian, little endian
l 可以有多个长度域“监听”某个特定的block“关闭”
l block可以嵌套或缠绕
一些基本调用:
l 在任何情况下主要的调用是一个s_push(buffer,size);
u 目前还没有s_pop();
l 字符串调用:
u s_string(“hi”);
u s_string_variable(“hi”);
l b_binary();
u 可以采用任何类型的剪、贴
16进制数字数据,不会有任何麻烦。
u 很干净地处理空格。
建立和取消SPIKE
l 必须处理的全局变量:
u set_cruuent_spike(*struct spike);
u spike_clear();
l Malloc函数
u Spike_new();
u Spike_free();
网络SPIKE调用:
l 基本的TCP/IP连接
u spike_tcp_connect(host,port);
u spike_send();
u spike_close_tcp();
l 基本的UDP连接
u spike_udo_connect(host,port);
u spike_send();
模糊框架SPIKE调用:
l s_string_variable(“”);
l s_string_repeat(“A”,5000);
u 等价于s_push(“perl –e ‘print “A” x 5000”);
l 支持while循环
u s_incrementfuzzstring();
u s_increamentfuzzvariable();
使用SPIKE模糊框架与使用perl脚本的优势:
l 自动修改长度值
l 可以通过s_binary()干净地处理二进制数据
l 已经知道许多不同类型的字符串可用于fuaastrings
l 很好地在C中集成了用于进行加密或做一些没有相应perl模块的libntlm或其他GPL’d库。
对一个未知协议使用SPIKE的过程
l 利用Ethereal来剪贴数据到s_binary()中;
l 用更深层的spike调用尽可能多的替换协议
u s_xdr_string();s_word();等等。
l 用size调用找到长度域并标识它们,并且调用s_block_start(),s_block_end()。
l 确认协议仍能正常工作
l 集成fuzzing框架(两个while循环)并让SPIKE去做令人厌烦的工作。
l 手工干预数据包,看看是否可以引起异常行为(先挂接ollydebug)
l 写利用程序
SPIKE脚本语言:
l C语言
l S_parse(“filename.spk”);
u 一行行加载文件并对它进行有限的C解析
u 使用dlopen()和dlsym()以及一些解编去调用任何在中
printf(“Hi %s%s\n”,”dave”,”\wtat’s up?”);
s_clear();
s_binary(“41 4243 44 45”);
发现的函数。
u 建立一个通用框架,然后用SPIKE脚本快速处理协议。
当前的演示SPIKE
l 针对web
l 支持MSRPC协议
l 其他一些杂项
针对非Web App的SPIKE程序
l msrpcfuzz
l Citrifuzz
l Quake halflife
快速入门:msrpcfuzz
l 首先使用DECDUMP(针对 Windows的rpcinfo)
l 然后选择一个程序和端口进行fuzz
u 给目标程序发送有效的,但是随机的数据
l 等待它崩溃
针对Web应用的SPIKE程序
l ntlm2/ntlm_brute
l webmitm
l makewebfuzz.pl
l webfuzz.c
l closed_source_web_server_fuzzer
l generic_web_server_fuzz
ntlm_brute和ntlm2
l 试图对NTLM认证的web服务器进行字典攻击
l 虽有些慢,但很容易并行
l 利用提供的do_ntlm_brute.sh非常容易进行
l ntlm2对于要求NTLM认证的页非常有用
Webmitm (SPIKE版本,不是dsniff版本)
l 透明代理(源于dsniff)
l 使用一个通用的http_request文件
l 可以进行SSL
l 重写Host: 头
l 带有“Connection: keep-alive”
Makewebfuzz.pl
l 从http_request文件中创建webfuzz.c文件。
l 被SPIKE Console 有魔力的、通用的.spk脚本取代,但仍然很有用。
Webfuzz
l 发送一个正常请求,但是在请求中的每一个变量都给予增量,并检查是否存在常见的漏洞。
一个标准请求
GET/login.asp?Username=Dave&Password=Justine
Host:bobsbagoffish.com
Content-Length:16
Server=whitbait
一个webfuzz请求
GET/login.asp?Username=./../etc/hosts%00&Password=Justine
Host:bobsbagoffish.com
Content-Length:16
Server=whitbait
Closed_source_webserver_fuzz
l 利用同样的fuzz字符串集合来查找常见web服务器的溢出,格式化串bug等等
l 对于一个CGI的严格手工测试也很有用
寻找SQL注入bug的自动处理过程
l odbcwebfuzz.sh
u 利用webmitm捕获的http_request构造一个字典
u 利用makewebfuzz.pl把这些内容编译到webfuzz中。
u 对web运行这些webfuzz
u 在结果中寻找有趣的错误(例如ODBC)
u 你刚好节约了20K
当自动处理失败时
l 这是一个指数级的问题
u 与商业化的软件不同,SPIKE的每一个部分都是开放的。
u SPIKE可以通过任何其他GPL代码进行扩展
u 我接受补丁
自动处理失败的例子
l 需要一些页面序列来引导的用户注册
u 使用SPIKE来自动击中前两个,然后 fuzz第三页上的每一个变量
l 使用了非“&”字符分隔变量的更加复杂的Web应用
l 要求请求中需要包含从前一页的输入中解析出的内容。
SPIKE控制台
l wxPython
u 跨平台
u 更漂亮
l Wizard帮助快速利用SPIKE的功能
l 当前是beta版,但是很有用
l 正在繁重的开发中
SPIKE的未来
l 改善SPIKE Console
l 增加SPIKE协议demo和更新
结论
l 对于大多数标准的WEB应用,SPIKE可以快速帮助你找到SQL 注入,溢出和格式化串bug
l SPIKE可以快速定制你的特殊需要
l 使用SPIKE进行逆向工程和fuzz二进制协议所需的时间比你可能找到的任何方法都少
l 今天免费下载
README
SPIKE是一个模糊生成工具。它有一套有趣的用于数据编组的通用API。在src目录里可以找到例子。这套API试图使复制未知协议这项工作对于逆项工程师和安全研究者变得更为简单。
这里包含了模糊器(虽然还没有彻底完成)和支持代码,它们是:
1、 webfuzz
webfuzz是许多小工具的集合,提供了一个灵活的,便于理解的web应用模糊化工具,特别地,提供了Dug Song的webmitm工具的一个修改版本。当用做透明代理的时候(通过修改攻击主机上的/etc/hosts文件,它在不同的文件中分别记录所有客户请求。它能够可靠地处理SSL,并且在编译中除了openssl以外不需要其他的库。这些记录下来的客户请求(或者通过一个网络sniffer,如ethereal,记录的请求)通过
SPIKE API被翻译成C语言的源文件。编译完成后,这些C文件(webfuzz.c)将自动地模糊化某个特定的CGI。Webfuzz.c文件作为一个源文件,可以被方便地修改或扩展来“close”某些特定的变量。因为SPIKE完全开放源代码,你完全有相当级别的灵活性和有效性,这是任何私有的、昂贵的东西所无法比拟的。SPIKE还包含了一个非常漂亮、高效的GUI来完成模糊化工作。另外,由于webfuzz完全依赖于浏览器来生成请求,它总是能够正确解析Java和其他脚本化的语言。
2、 msrpcfuzz
当与dcetest相配合,msrpcfuzz试图演习一个任意的ncan_tcp程序。它发送随机的变量。如果端口突然关闭了,你就可能找到了一个潜在的bug。
如何使用SPIKE API
Q:我有一个新的二进制协议,我想要快速复制它,应该怎样做?
A:一般来说,最直接的做法是拿来用ethereal转储的数据包,从中发送所有你希望发送的数据包,你应该这样做:
Spike_clear(); /* 清理spike */ S_binary(“<在这儿剪贴数据包>”); S_send();
对于服务器发送给你的每一个数据包,执行read_packet() (取自quake.c)。它将很好地把信息打印出来,使你能够确认你所做的是正确的。以后你还可以对这些信息进行解析,如果你认为有必要的话。或者你可以只做一个s_fd_wait();然后read(),手工执行可以丢弃一些数据包。如果spike没有给你足够的等待时间,只要编辑spike.c顶部的#define常数。但是缺省的值已经可以满足大多数人。
现在你已经连接了服务器并把转储的数据包发送到服务器,使它们进行对话,你需要找到“size”的位置,它有可能散落在整个协议中。Ethereal可能已经了解到某些长度信息,或者它们很容易用眼睛找出来。
使用这些“size”索引开始把数据包分解成数据块。如果可能的话使用s_调用,而不是s_binary(如s_string,s_xdr_string,等等)。一个数据块可以是一个字符串,也可以是一个字符串和一些s_binary()数据的组合,或者任何其他什么东西。
一旦你得到了对实际使用的rpc协议的一个合理的逼近,把所有的东西扔到一个模糊框架中(从./closed中剪贴,或其他自动化的fuzzer),或手工运行、编辑、再运行你的spike程序,利用各种长字符串(使用s_string_repeat(“A”,5000),就好象perl –e ‘print “A” x 5000’)
不要忘记在另一端挂接一个调试器并确认你捕获了所有的例外。: >
提示和技巧:
Q:我想要快速剪贴一个POST数据体到spike中。我应该怎样做呢?
A:在每一个URL变量上使用s_string_variables()函数。如:
S_string_variables(‘&’,”username=bob&password=feet”);
这等价于:
“s_string(“username=”); s_string_variable(“bob”); s_String(“&password=”); s_string_variable(“feet”);
Q:我如何让spike进行针对web服务器的fuzzing,一个 ./closed?
A:下面是一个样板的循环,它可以把一个http请求发送到服务器。
signal (SIGPIPE, SIG_IGN); /*忽略服务器关闭连接的情况 */ our_spike = new_spike (); /*does malloc fun */ s_init_fuzzing (); /*执行spike中的一些初始化工作 */ setspike (our_spike); /* 使spike.c的内部指针current_spike指向这个spike。所有的spike.c函数,如s_string(),s_binary()等等都在setspike所选择的spike之上工作。你可以保持多个spike,并利用setspikea90在它们之间切换*/ s_resetfuzzvariable (); /* 把内部的fuzz变量设置为0。你压入的每一个变量都被计数。你所做的第一个s_string_variable()调用将是变量0,下一个是变量1。通过这种方法spike.c我们在fuzzing哪个变量 */ while (!s_didlastvariable ()) /* 仅当在最后一条路径上fuzzed你设置的最后一个变量时为真*/ /*true only if on the last path we fuzzed the last variable youset */ { s_resetfuzzstring (); /*fuzzstrings 是你fuzz的实际字符串——一长串“A”,一长串“1”等等。我们通过s_increment调用来进行重复*/ /*fuzzstrings are the actual strings you fuzz with - longstrings of A's, long strings of 1's, etc. We iterate through them withs_increment calls. */ /*theorectically, thezeroth fuzz string is no change */ /*理论上第0个fuzz没有变化 */ /* 与didlastvariable类似,但是是针对fuzzstrings的*/ while(!s_didlastfuzzstring ()) { /* spike_clear()是一个老家伙,它清除一个SPIKE的大部分状态,特别是清除缓冲区信息。但是有些东西没有清除,如变量信息*/ spike_clear (); /* 必须调用setfirstvariable。对于有些例程,它告诉spike什么时候放置一个“?”来进行分隔。它实际上并不那么重要 :>*/ s_setfirstvariable (); /* 数据流 */ push_your_HTTP_request_here(); /*参考 ./closed.c 的一个实际例子*/ /* 对于定制工作,你可以使用 ./closed.c作为一个shell,并仅仅改变你的数据流为 s_push_variables(‘&’,whateverstringyouwant); /*spike_send_tcp完成连接和发送一个spike_send操作,目标可以是127.0.0.1或localhost */ if (spike_send_tcp(target, port) == 0) { printf ("Couldn't connect to host orsend data!\r\n"); /*exit(-1); */ } /* fuzzstring = fuzzstring + 1,我们前进到下一个用于填充fuzz变量的字符串。*/ s_incrementfuzzstring(); /*一些循环控制变量*/ notfin = 1; retval = 1; /* 下面读取服务器的返回,如果等待的时间过长就断开连接 */ while (retval&& notfin) { memset (buffer, 0x00, sizeof (buffer)); /* s_fd_wait()基本上是一个select()调用,如果到了关闭连接的时候它返回0*/ notfin = s_fd_wait (); if (!notfin) { printf("Server didn't answer in time limit\n"); break; } retval = read (our_spike->fd, buffer,2500); printf ("**%.2500s**\n", buffer); } /*send fins!*/ spike_close_tcp (); } /*每一个fuzz字符串的结尾*/ /*下一个变量*/ s_incrementfuzzvariable(); } /*每一个变量的结尾*/