Fuzzing測試中對於SPIKE框架的應用(一)

該隱的星球發表於2018-01-17

原文連結:http://blog.csdn.net/xreztento/article/details/39213723
一、Fuzzing概述
模糊測試——我們常常稱其為Fuzzing,被定義為一種通過提供非預期的輸入並監視異常結果來發現軟體故障的方法。 Fuzzing的樂趣在於探索一些出人意料或隱藏很深的安全性缺陷,這些安全性缺陷不乏如緩衝區溢位、伺服器異常崩潰等,一旦確認這些缺陷事實存在,它們往往都是致命性的。當然這並不限制通過為Fuzzing模糊器精心構造一系列的規則和資料庫來發現諸如SQL隱碼攻擊、XSS攻擊、目錄遍歷/弱訪問控制、弱認證、遠端命令執行等缺陷,規則設計的越加智慧、針對已知漏洞的資料字典內容越加豐富,Fuzzing測試的效率和結果也會越理想。當然,也同樣可以使用暴力構造畸形資料的方式來生成Fuzzing資料。
Fuzzing測試需要藉助於上文提到的Fuzzing模糊器,最常見的Fuzzing模糊器主要有兩類:
(1)基於變異的模糊器,這種模糊器對已有資料樣本應用變異技術以建立測試用例;
(2)基於生成的模糊器,這種模糊器通過對目標協議或檔案格式建模的方法從頭開始產生測試用例。
二、SPIKE概述及其特點
SPIKE Fuzzer Creation Kit作為最著名的Fuzzing模糊器框架(spike fuzzing framework),是immunitysec公司的Dave Aitel寫的一個黑盒安全性測試工具集SPIKE Suite Tools的一個重要組成部分,為我們提供了一個包含長字串、帶有格式串的字串、大整數、負數、各種畸形字元等的Fuzzing資料庫,一套基於Block-Based Protocol Analysis方法的測試框架以及豐富的API,我們不僅可以利用SPIKE提供的各類實用指令碼直接實現Fuzzing測試,更可以利用框架,對API進行輕量級封裝,從而構建屬於我們自己的模糊器。
我們可以從http://www.immunitysec.com/resources-freesoftware.shtml網站獲取並瞭解它。(或http://download.csdn.net/detail/xreztento/9593293
SPIKE對於尋找安全性缺陷或脆弱性具備以下特點,瞭解這些對於利用該框架構建模糊器非常重要:
(1)利用它可以便捷快速地重構一個複雜的二進位制協議;
(2)利用它可以很容易地使用Fuzzing資料來混亂協議結構和內容;
(3)SPIKE發展出了一套關於Fuzz的基礎理論,該理論總結為構造一些特殊的數字或者字串作為程式的輸入,檢查程式是否能夠處理這些異常資料;
(4)利用已知的安全缺陷測試新的程式。
SPIKE的資料域類似於FIFO佇列或稱其為“BufferClass”的結構,格式支援多種的資料型別:Word、halfword、string、Big endian、 little endian 等;
主要包括三種基礎的Fuzzing資料構造的方法:
(1)通過呼叫s_push(buffer,size)向SPIKE資料域下方填充資料,支援基於perl語言的指令碼s_push(“perl -e ‘print “A” x5000’”);的實現方式;
(2)通過呼叫字串資料建構函式s_string(“hi”);s_string_variable(“hi”);向SPIKE資料域下方填充普通字串或帶有Fuzzing變數字串資料;
(3)通過呼叫二進位制資料建構函式s_binary(“\x41 4141 0x41 41 00”); 向SPIKE資料域下方填充二進位制資料,此方法支援直接通過剪下和貼上16進位制資料的方式,並能自動清除中間夾雜的空格。
另外,SPIKE具備一種獨特的資料結構,該結構支援對長度與塊的支援,從而可以自動填充一些協議中要求的長度域,主要的實現方式是通過“監聽”一段特殊的塊的結束標識來完成的:
[plain] view plain copy
s_size_string(“post”,5);
s_block_start(“post”);
s_string_variable(“user=bob”);
s_block_end(“post”);
值得注意的是上述的填充方式支援在程式碼中的巢狀或交織,這種實現極具便利性。
SPIKE還能夠輕鬆的生成網路編碼或常見的訊號編集資料,如利用這樣的函式:s_xdr_string();暴力生成一些重複地資料,如利用這樣的函式:s_string_repeat(“A”,5000);
三、如何使用SPIKE框架
(1)初始化和銷燬一個SPIKE
將其處理為一個全域性作用域變數:
[plain] view plain copy
set_current_spike(*struct spike);
spike_clear();
記憶體分配:
[cpp] view plain copy
spike_new();
spike_free();
(2)SPIKE的網路功能
基礎的TCP聯結器:
[cpp] view plain copy
spike_tcp_connect(host,port);
spike_send();
spike_close_tcp();
基礎的UDP聯結器:
[cpp] view plain copy
spike_udp_connect(host,port);
spike_send();
(3)2 while() loops模糊器模板
利用s_incrementfuzzvariable()和s_incrementfuzzstring()組成被稱之為2 while() loops的方式實現持續迭代的Fuzzing模糊器,下面是一個該模糊器標準的實現模板:
[cpp] view plain copy
struct spike *our_spike;
our_spike = new_spike();
setspike(our_spike);//設定本上下文環境spike為our_spike

s_init_fuzzing();//初始化fuzzing字串
s_resetfuzzvariable();//重置fuzzing變數
while(!s_didlastvariable()) {//迴圈直到最後一個fuzzing變數
s_resetfuzzstring();//重置fuzzing字串
while(!s_didlastfuzzstring()){//迴圈直到最後一個fuzzing字串
spike_clear();
s_setfirstvariable();//從第一個變數開始

    /*構造Fuzzing資料*/  
    s_string_variables('&',"/uid=bob&passwd=bob");//以&字元為分割符,將兩處的bob設定為fuzzing變數  
    //或用以下方式實現同樣效果  
    s_string("/uid=");  
    s_string_variable("bob");  
    s_string("&passwd=");  
    s_string_variable("bob");  

    /*獲取Fuzzing資料*/  
    s_get_databuf();  

    /*利用Fuzzing資料進行測試 
     *do_something(); 
     */  

    s_incrementfuzzstring();//fuzzing字串+1  
}  
s_incrementfuzzvariable();//fuzzing變數+1  

}
spike_free(our_spike);
四、輕量級封裝
通過2 while() loops模糊器模板從頭構建一個針對HTTP協議的模糊器,可以直接參考SPIKE框架中提供的如post_fuzz.c、post_spike.c等原始碼,但這樣的模糊器嚴重缺乏靈活性,並需要模糊器使用人員深入到程式碼內部進行對Fuzzing資料的設定和調整,輕量級封裝的有易之處在於使得我們所構建的模糊器儘量的靈活或使用簡潔,為模糊器使用人員帶來便利。以下的例子是一類典型的封裝思路,要達到的主要目標是構建Fuzzing資料的過程在2 while() loops結構的外部,構造方法簡單易用:
[plain] view plain copy
/*
* xf_flexed_fuzz_http_request.c
*
* Created on: 2014年9月12日
* Author: xreztento@vip.sina.com
*/

include

include

include

include

include

include

include

include

include “spike.h”

include “hdebug.h”

include “tcpstuff.h”

include “udpstuff.h”

define MAX_FUZZ_NUM 100//最大的fuzzing引數數量,實際為MAX_FUZZ_NUM / 2

define MAX_BUFFER_SIZE 102400//HTTP收發最大字元數

define MAX_BUFFER_WRITE_SIZE 2500//一次讀取的最大字元數

define CRLF “\r\n”

define PARAM_LEFT_EDGE ‘{‘//引數左邊距

define PARAM_RIGHT_EDGE ‘}’//引數右邊距

typedef struct FORMAT_LINE{
char *point[2];//用於flexed_fuzz的格式化行,通過s_string(point[0])和s_string_variable(point[1])實現對spike的拼接
} FORMAT_LINE;

static FORMAT_LINE line_array[MAX_FUZZ_NUM];

static int
splitline(char *in, char **pointer);

static int
formatline(char *in, char **pointer);

extern int
read_http_response_status_num(char *in);

char in[] =
“GET /test.htm?uid={xreztento}&passwd={passwd} HTTP/1.1\r\n”
“Host: localhost\r\n”
“User-Agent: Mozilla/5.0 (X11; Linux i686; rv:10.0) Gecko/20100101 Firefox/10.0\r\n”
“Accept: {text/html,application/xhtml+xml,application/xml;q=0.9,/;q=0.8}\r\n”
“Accept-Language: zh-cn,zh;q=0.5\r\n”
“Accept-Encoding: gzip, deflate\r\n”
“Proxy-Connection: keep-alive\r\n”
“Cookie: BAIDUID=2CA5FD322D09701E3EC025BFF9AB62DB:FG=1; BD_UPN=1333; BD_HOME=0; BD_CK_SAM=1; H_PS_PSSID=7544_1443_7802_8057_6505_7634_6018_7893_8123_7606_7799_8035_7790_7941_8115\r\n”
“Cache-Control: max-age=0\r\n”;

int
xf_flexed_fuzz_http_request(char *name, char *target, int port, char *request){
int i;
char *pointer[MAX_FUZZ_NUM];//儲存格式化後的字元
int row = formatline(in, pointer);//格式化HTTP請求,並返回格式化結構的行數

char buffer[MAX_BUFFER_SIZE];//用於儲存HTTP請求和響應資料  

struct spike *our_spike;  

unsigned long retval;//判斷伺服器是否響應結束  
int notfin;//判斷是否伺服器超時未返回響應資料  
int first;//用於控制一次HTTP請求過程  

our_spike = new_spike();  
setspike(our_spike);//設定本上下文環境spike為our_spike  

s_init_fuzzing();//初始化fuzzing字串  

signal(SIGPIPE,SIG_IGN);  

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

s_resetfuzzvariable();//重置fuzzing變數  

while(!s_didlastvariable()) {//迴圈直到最後一個fuzzing變數  
    s_resetfuzzstring();//重置fuzzing字串  
    while(!s_didlastfuzzstring()){//迴圈直到最後一個fuzzing字串  
        spike_clear();  
        s_setfirstvariable();  

        //fuzzing字串附值  
        for(i = 0;i < row; i++){  
            s_string(line_array[i].point[0]);  
            s_string_variable((unsigned char *)line_array[i].point[1]);  
        }  
        s_string(line_array[row].point[0]);  
        s_string(CRLF);  

        sleep(1);  

        printf("Sending to %s on port %d\r\n", target, port);  

        //傳送HTTP請求到伺服器  
        if(spike_send_tcp(target, port) < 0){  
            printf("Couldn't connect to host or send data!\n");  
            exit(-1);  
        }  
        //buffer儲存HTTP請求資料  
        memset(buffer, 0x00, sizeof(buffer));  
        memcpy(buffer, s_get_databuf(), s_get_size());  


        printf("Request:\n%.2500s\nEndRequest\n",buffer);  

        printf("reading\r\n");  

        notfin = 1;  
        retval = 1;  
        first = 1;  

        memset(buffer, 0x00, sizeof(buffer));  
        while(retval && notfin){  
            char tmp[MAX_BUFFER_WRITE_SIZE];  
            memset(tmp, 0x00, sizeof(tmp));  
            notfin = s_fd_wait();//等待讀取資料  

            if (!notfin) {  
                printf("Server didn't answer in time limit\n");  
                break;  
            }  
            //讀取HTTP響應資料  
            retval = read(our_spike->fd, tmp, MAX_BUFFER_WRITE_SIZE);  
            if (first && (retval == -1 || retval == 0) ) {  
                fprintf(stderr,"Server closed connection!\n");  
            }  
            first = 0;  
            if (retval) {  
                //printf("%s\n", tmp);  
                strcat(buffer, tmp);  
            }  
        }  

        //printf("%s\n", buffer);  

if 1

        int status = read_http_response_status_num(buffer);  
        printf("Response status:%d\n",status);  
        //根據HTTP Response Status做處理  
        //do_something();  

endif

        printf("End response\n");  

        s_incrementfuzzstring();//fuzzing字串+1  
        spike_close_tcp();  
        if(row == 0){  
            break;  
        }  
    }  
    if(row == 0){  
        break;  
    }  
    s_incrementfuzzvariable();//fuzzing變數+1  
}  
    spike_free(our_spike);  
    return 0;  

}

/**
* 將字串按FORMAT_LINE結構體陣列格式進行格式化
*/
static int
formatline(char *in, char **pointer){
int i;
int j;
int line_num = splitline(in, pointer);//分割後每個子字串的指標數量

int row_num = ( line_num + 1) / 2;//FORMAT_LINE結構體陣列元素數  
for(i = 0;i <= row_num;i++) {  
    j = i * 2;  
    line_array[i].point[0] = pointer[j];  
    line_array[i].point[1] = pointer[j+1];  
}  

return row_num;//需要Fuzzing的數量  

}

/**
* 將輸入字串按左右邊界字元進行拆分,將左右邊界替換為\0以作為陣列分割
*param in 被分割字串
*param pointer 分割後子字串指標陣列首元素的指標
*/
static int
splitline(char *in, char **pointer){
int i, j;
int k = 0;//指標陣列索引

pointer[k] = in;//陣列第一個元素指向被分割字串首地址  
int n = strlen(in);  

for(i = 0; i < n; i++) {  
    if(*(in + i) == PARAM_LEFT_EDGE) {//當發現左邊界時  
        *(in + i) = '\0';//將邊界替換成字串結束符  
     k++;//索引+1  
     pointer[k] = in +i +1;//指向左邊界對應的下一個字元地址  
     for(j = i + 1; j < n; j++) {//從左邊界對應的下一個字元開始迴圈  
            if(*(in + j) == PARAM_RIGHT_EDGE) {//當發現右邊界時  
                *(in + j) = '\0';//將邊界替換成字串結束符  
           break;//結束迴圈  
            }  
        }  
     k++;//索引+1  
     pointer[k] = in + j + 1;//指向右邊界對應的下一個字元地址  
     i = j;  
    }  
}  

return k;
}

int
main(){
xf_flexed_fuzz_http_request(“my_fuzzing”,”localhost”,80,in);
return 0;
}

[plain] view plain copy
/*
* xf_fuzz_common.c
*
* Created on: 2014年9月12日
* Author: xreztento@vip.sina.com
*/

include

include

define FRIST_ROW_MAX_SIZE 100

/*
* 讀取HTTP響應資料第一行
*/
char *read_http_response_frist_row(char *in){
static char frist_row[FRIST_ROW_MAX_SIZE];
int i;
for(i = 0;i < FRIST_ROW_MAX_SIZE; i++){
if(*(in + i) != ‘\r’){
frist_row[i] = *(in + i);
} else {
break;
}
}
return frist_row;

}

/*
*讀取HTTP響應狀態碼
*/
int read_http_response_status_num(char *in){
char *frist_row = read_http_response_frist_row(in);
char status_num_str[3];
status_num_str[0] = frist_row[9],
status_num_str[1] = frist_row[10],
status_num_str[2] = frist_row[11];
int status_num = atoi(status_num_str);

return status_num;  

}


該構造器直接使用”{}”完成對符合HTTP協議GET方法的HTTP Request Header資料進行SPIKE Fuzzing變數的設定,通過呼叫int xf_flexed_fuzz_http_request(char*name,char *target,intport, char *request)實現Fuzzing測試。
尾聲:
本文章對Fuzzing技術和應用SPIKE框架進行了簡單介紹,目的是為了幫助讀者更多的對其有所瞭解,也希望能夠在此基礎上開拓思路,構建屬於自己的模糊器。但是對於具體怎麼安裝使用沒有介紹。讀者可以直接使用本文中提供的封裝案例執行Fuzzing測試。後續章節將試圖通過不斷地對SPIKE框架進行封裝,從而得到更多通用且實用的模糊器方法。

相關文章