【文章作者】: condor
【作者邮箱】: cracker@vip.qq.com
【作者主页】: http://hi.baidu.com/linshifei
【作者声明】: 只是感兴趣,没有其他目的。失误之处敬请诸位大侠赐教!

第一篇,顺便做下科普,   Fuzz 由来(忘了哪里看到了): 在1980到1990之间,三个研究者Barton Miller,Lars Fredriksen和Bryan So致力于研究UNIX命令行程序的完整性。
   在一个暴风雨过后的夜晚,其中的一位研究者通过拔号线路连到远程服务器,在准备运行某个Unix程序时,由于线路噪音,一些随机数据代替了他的输入被发送到Unix程序里,在程序执行时,因为这些随机数据的存在,导致程序产生core dump。根据这个发现,三位研究者开发了FUZZ系统,用于生成伪随机输入数据来测试程序的完整性。现在,Fuzz已成为测试软件安全漏洞的一个重要部分。

SPIKE是一套C函数接口的API(Primitives ),注意没有windows版本,构造的基本语法: 
静态内容:s_string() s_binary(),s_intelword(),s_string_repeat() 
动态内容:s_string_variable(), 
网络控制:spike_send_udp() spike_listen_udp() spike_send_tcp() 
Block控制:s_binary_block_size_intel_word () s_block_end () 
变量控制:s_incrementfuzzstring () s_incrementfuzzvariable ()

接下来主要是一点读书笔记,

安装:
tar zfv SPIKE2.9.tar
先运行aclocal
然后运行 automake && autoheader && autoconf && ./configure

make

然后就生成很现成了(去掉。c以后):

root@condor:/usr/local/apple/SPIKE/SPIKE/src# ls *.c -l|grep fuzz
-rwxrwxr-x 1 500 500 12705 Apr 13 2004 closed_source_web_server_fuzz.c
-rwxrwxr-x 1 500 500 3664 Jan 15 2004 form_fuzz.c
-rwxrwxr-x 1 500 500 5953 Jan 15 2004 generic_web_server_fuzz.c
-rwxrwxr-x 1 500 500 6388 Jan 15 2004 generic_web_server_fuzz2.c
-rwxrwxr-x 1 500 500 12408 Jan 15 2004 msrpcfuzz.c
-rwxrwxr-x 1 500 500 12760 Jan 15 2004 msrpcfuzz_udp.c
-rwxrwxr-x 1 500 500 4242 Jan 15 2004 oldmsrpcfuzz.c
-rwxrwxr-x 1 500 500 7461 Jan 15 2004 post_fuzz.c
-rwxrwxr-x 1 500 500 8490 Jan 15 2004 sunrpcfuzz.c
-rwxrwxr-x 1 500 500    25 Jan 15 2004 webfuzz.c
-rwxrwxr-x 1 500 500    25 Jan 15 2004 webfuzz_blank.c
-rwxrwxr-x 1 500 500 1190 Jan 15 2004 webfuzzpostlude.c
-rwxrwxr-x 1 500 500 1290 Jan 15 2004 webfuzzprelude.c

好东西在 audits目录下:
root@condor:/usr/local/apple/SPIKE/SPIKE/src/audits# ls -l
total 68
drwxrwxr-x 2 500 500 4096 Apr 14 2004 BIZTALK2000
drwxrwxr-x 2 500 500 4096 Apr 14 2004 CIFS
drwxrwxr-x 2 500 500 4096 Apr 14 2004 COMPAQ
drwxrwxr-x 2 500 500 4096 Apr 14 2004 FTPD
drwxrwxr-x 2 500 500 4096 Apr 14 2004 H323
drwxrwxr-x 2 500 500 4096 Apr 14 2004 IMAP
drwxrwxr-x 2 500 500 4096 Apr 14 2004 MSContentManagementServer
drwxrwxr-x 3 500 500 4096 Apr 14 2004 MSSQL
drwxrwxr-x 2 500 500 4096 Apr 14 2004 ORACLE
drwxrwxr-x 2 500 500 4096 Apr 14 2004 POP3
drwxrwxr-x 2 500 500 4096 Apr 14 2004 PPTP
drwxrwxr-x 2 500 500 4096 Apr 14 2004 RealServer
drwxrwxr-x 2 500 500 4096 Nov 9 08:07 SMTP
drwxrwxr-x 2 500 500 4096 Apr 14 2004 SSL
drwxrwxr-x 2 500 500 4096 Apr 14 2004 UPNP
drwxrwxr-x 2 500 500 4096 Apr 14 2004 exchange2K
drwxrwxr-x 2 500 500 4096 Apr 14 2004 lotus

来看一个 ,都是脚本,。spk是脚本,脚本和上面的c程序是等价 效果的。

root@condor:/usr/local/apple/SPIKE/SPIKE/src/audits/SMTP# vim smtp3.spk

s_string_variable("HELO");
s_string(" ");
s_string_variable("localhost");
s_string("\r\n");
s_string("MAIL-FROM: <");
s_string_variable("");
s_string(">");
s_string("\r\n");
s_string("RCPT-TO: postmaster@company.mail");
s_string("DATA\r\n");
s_string("Message-ID: 123\r\n");
s_string("ASDF\r\n");
s_string("ASDF\r\n");
s_string(".\r\n");
s_string("QUIT\r\n");

里面有基于 GTk+ wxPython 图像界面 python程序 SPIKE_Console.py 
没有环境跑不起来

来看个例子 一般fuzz调用流程 ,快速入门:

main (int argc, char ** argv)
{
signal (SIGPIPE, SIG_IGN); /*ignore when the server closes the connection on us */
our_spike = new_spike (); /*一个新的spike指针 */
s_init_fuzzing ();   /*do some basic initialization in spike.c */
setspike (our_spike);   /*设置当前指针. 接下来调用的其他函数s_string(), s_binary()等将作用在这个指针上. 可以有多个 spike指针,调用setspike() 切换 */
s_resetfuzzvariable ();   /*清 fuzz变量为 0 ,s_string_variable() 调用将加1*/

while (!s_didlastvariable ()) /*did last variable 直到最后一个 fuzz变量 */
{
   s_resetfuzzstring (); /*重置 fuzz使用的stirng ,一般是大量的 'A' */
  
   /* 是否是最后一个fuzzstrings*/
   while (!s_didlastfuzzstring ())
   {
   
   /* 清空spike的内部状态 */
    spike_clear ();
    /*设置第一个 变量 ,有时候为‘?’*/
    s_setfirstvariable ();
   
    /*PAYLOAD*/
    push_your_HTTP_request_here(); /*通过调用s_push_variables('&',whateverstringyouwant)等添加你的实际包内容*/


    if (spike_send_tcp (target, port) == 0)
    {
     printf ("Couldn't connect to host or send data!\r\n");
     /*exit(-1); */
    }
    /*fuzzstring = fuzzstring + 1 - we advance to the next string
    使用fuzzstring 填充当前fuzz变量*/
    s_incrementfuzzstring ();
    /*some loop control variables*/
    notfin = 1;
    retval = 1;
   
    /*this next bit just reads back what the server sends to us - but
    if it takes too long, we just disconnect*/
   
    while (retval && notfin)
    {
    
     memset (buffer, 0x00, sizeof (buffer));
    
     /*s_fd_wait() is a select() call basically. It returns 0 if it's time
     to close the connection*/
     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 ();
   }     /*end for each fuzz string */
  
   /*next variable*/
   s_incrementfuzzvariable ();
}     /*end for each variable */
}


一些函数技巧
spike_send_tcp() 我的理解是长连接 ,需要 spike_close_tcp()来断开
spike_send() 短链接,

可以直接使用抓包数据
spike_clear(); /*nice clean spike*/
s_binary("使用sniffer工具抓到包直接贴在这里>");
s_send();

构造重复数据:
s_string_repeat("A",5000) - just like perl -e 'print "A" x 5000'


构造post数据方法,以下是等价的:
s_string_variables('&',"username=bob&password=feet");
s_string_variable("bob"); s_string("&password="); s_string_varialbe("feet");

其他学习建议 就是跑一跑例子,抓包看看 变化规律,就会明白 fuzz的过程。


一个完整例子,结束下block 这个重要的概念 ,基于上面的流程框架 ,测试http post 。

/*just a quicky demonstration of using SPIKE to send a POST command
to a web server*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h> /*for memset*/
#include <sys/types.h>
#include <sys/socket.h>


#include "spike.h"
#include "hdebug.h"
#include "tcpstuff.h"


void
usage()
{
fprintf(stderr,"Usage: ./post_spike target port\r\n");
exit(-1);
}


int
main (int argc, char ** argv)
{
char * target;
char buffer[1500000];
char *url,*host;
int port;

struct spike * our_spike;
unsigned long retval;
int i;

if (argc!=3)
    {
   usage();
    }

target=argv[1];
printf("Target is %s\r\n",argv[1]);

port=atoi(argv[2]);

our_spike=new_spike();


if (our_spike==NULL)
    {
   fprintf(stderr,"Malloc failed trying to allocate a spike.\r\n");
   exit(-1);
    }

setspike(our_spike);

memset(buffer,0x41,sizeof(buffer));
buffer[sizeof(buffer)]=0;


for (i=0; i<500; i+=4)
    {
   memcpy(buffer+i,"%25s",4);
    }

/* 
Allow: OPTIONS, TRACE, GET, HEAD, DELETE, PUT, COPY, MOVE, PROPFIND, PROPPATCH, SEARCH, SUBSCRIBE, UNSUBSCRIBE, POLL, BDELETE, BCOPY, BMOVE, BPROPPATCH, BPROPFIND, LOCK, UNLOCK
*/

buffer[140000]=0;

printf("Buffer size = %d\r\n",strlen(buffer));

host=strdup("10.3.8.25");
url=strdup("/exchange/bob/Drafts/No%20Subject-4.EML/?Cmd=addattach&Embedded=0");
/*takes a url, host, and posts it*/
s_string("POST ");
s_string(url);
// s_string_repeat("%n",15000);

s_string(" HTTP/1.1\r\n");
s_string("Host: ");
s_string(host);
s_string("\r\n");
s_string("Authorization: Basic Ym9iOmJvYg==\r\n");
//s_string("Cookie: sessionid=b1c4d131-9439-4a7b-894f-7185ca8c6695,0x9\n");

s_string("Cookie: sessionid=03570e97-62ab-4372-bd98-f7faac2e2619,0x409; sessionid=374642e5-8427-4be5-9bb2-1d90c01ed28c,0x409; ASPSESSIONIDQQGQQNOC=");
s_string("OIJHFNJBFLCAAPHINMAJGHJM\r\n");

s_string("Referer: http://10.3.8.25/exchange/bob/Drafts/No%20Subject-4.EML?Cmd=editattach\r\n");
s_string("Connection: Keep-Alive\r\n");
s_string("User-Agent: Mozilla/4.76 [en] (X11; U; Linux 2.4.2-2 i686)\r\n");
s_string("Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, image/png, */*\r\n");
s_string("Accept-Encoding: gzip\r\n");
s_string("Accept-Language: en\r\n");
s_string("Accept-Charset: iso-8859-1,*,utf-8\r\n");
s_string("Content-type: multipart"); 

s_string("/form-data; ");
s_string("boundary=");

s_string("---------------------------42423833519577477931714636915");

s_string("\r\n");

s_string("Content-length: ");

//这个位置是个 字符 表示 post block 段的大小
s_blocksize_string("post",7);
s_string("\r\n\r\n");

//post 端定义开始
s_block_start("post");

/*start post here*/
s_string("-----------------------------42423833519577477931714636915");
s_string("\r\n");
s_string("Content-Disposition: ");

s_string("form-data; name=\"attachFile");

s_string("\"; filename=\"/tmp/asdf");
s_string("\"\r\n");
s_string("Content-Type: ");

s_string("application/octet-stream\r\n");

s_string("Content-Transfer-Encoding: ");

s_string("binary\r\n");
s_string("-----------------------------42423833519577477931714636915--\r\n");
/*结束 POST*/

s_block_end("post");

/*
s_print_buffer();
s_printf_buffer();
*/
printf("Sending to %s on port %d\r\n",target,port);
if (spike_send_tcp(target,port)<0)
    {
   printf("Couldn't connect to host or send data!\r\n");
   exit(-1);
    }


printf("reading\r\n");
memset(buffer,0x00,sizeof(buffer));
retval=1;
while (retval!=-1)
    {
   s_fd_wait();
   retval=read(our_spike->fd,buffer,1500);
   printf("%s",buffer);
    }
return 0;
}