SQLMap的前世今生(Part1)
http://www.freebuf.com/sectool/77948.html
一、前言
談到SQL註入,第一時間就會想到神器SQLMAP,SQLMap是一款用來檢測與利用的SQL註入開源工具。那麽SQLMap在掃描SQL的邏輯到底是怎樣實現的呢,接下來就探討下SQLMap的掃描邏輯,通過了解SQLMap的掃描邏輯打造一款屬於自己的SQL掃描工具。
二、SQL掃描規則
要了解SQLMap的掃描規則,也就是Payload,那麼到底Payload是哪裏來,是根據什麽邏輯生成的呢,接下來必須先了解幾個文件的,SQLMap的掃描規則文件位於\xml文件夾中,其中boundaries.xml與Payloads文件夾則為SQLMap的掃描規則所在,\xml\payloads中的6個文件,裏面的6個文件分別是存放著不同註入手法的PAYLOAD。
那麽就必須了解兩個格式,一是boundary文件,一是payloads。
例子:
<boundary>
<level>1</level>
<clause>1</clause>
<where>1,2</where>
<ptype>1</ptype>
<prefix>‘</prefix>
<suffix> AND ‘[RANDSTR]‘=‘[RANDSTR]</suffix>
</boundary>
1. clause與where屬性
這兩個元素的作用是限制boundary所使用的範圍,可以理解成當且僅當某個boundary元素的where節點的值包含test元素的子節點,clause節點的值包含test元素的子節點的時候,該boundary才能和當前的test匹配,從而進一步生成payload。
2. prefix與suffix屬性
要理解這兩個屬性的作用,那麼就先利用一段代碼去講解。
function getattachtablebypid($pid) {
$tableid = DB::result_first("SELECT tableid FROM ".DB::table(‘forum_attachment‘)." WHERE pid=‘$pid‘ LIMIT 1");
return ‘forum_attachment_‘.($tableid >= 0 && $tableid < 10 ? intval($tableid) : ‘unused‘);
}
通過代碼我們可以知道pid參與了SQL語句的拼接,那麼如果我們輸入的pid為‘ AND ‘test‘ = ‘test呢,那麼最終拼接起來的SQL語句應該為:
SELECT tableid FROM ".DB::table(‘forum_attachment‘)." WHERE pid=‘‘ AND ‘test‘ = ‘test‘ LIMIT 1
所以如果我們輸入的是‘ AND ‘test‘ = ‘test,那麼最終拼接起來的SQL語句同樣是合法的。那麼我們就可以把所測試的Payload放到prefix與suffix中間,使之最終的SQL合法,從而進行註入測試,所以通過了解,prefix與suffix的作用就是為了截斷SQL的語句,從而讓最終的Payload合法。
至此boundary文件的作用已經講解完了,接下來就是payload的講解了。
<test>
<title>MySQL >= 5.0 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause</title>
<stype>2</stype>
<level>1</level>
<risk>1</risk>
<clause>1,2,3</clause>
<where>1</where>
<vector>AND (SELECT [RANDNUM] FROM(SELECT COUNT(*),CONCAT(‘[DELIMITER_START]‘,([QUERY]),‘[DELIMITER_STOP]‘,FLOOR(RAND(0)*2))x FROM INFORMATION_SCHEMA.CHARACTER_SETS GROUP BY x)a)</vector>
<request><!-- These work as good as ELT(), but are longer<payload>AND (SELECT [RANDNUM] FROM(SELECT COUNT(*),CONCAT(‘[DELIMITER_START]‘,(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN 1 ELSE 0 END)),‘[DELIMITER_STOP]‘,FLOOR(RAND(0)*2))x FROM INFORMATION_SCHEMA.CHARACTER_SETS GROUP BY x)a)</payload><payload>AND (SELECT [RANDNUM] FROM(SELECT COUNT(*),CONCAT(‘[DELIMITER_START]‘,(SELECT (MAKE_SET([RANDNUM]=[RANDNUM],1))),‘[DELIMITER_STOP]‘,FLOOR(RAND(0)*2))x FROM INFORMATION_SCHEMA.CHARACTER_SETS GROUP BY x)a)</payload>-->
<payload>AND (SELECT [RANDNUM] FROM(SELECT COUNT(*),CONCAT(‘[DELIMITER_START]‘,(SELECT (ELT([RANDNUM]=[RANDNUM],1))),‘[DELIMITER_STOP]‘,FLOOR(RAND(0)*2))x FROM INFORMATION_SCHEMA.CHARACTER_SETS GROUP BY x)a)</payload>
</request>
<response>
<grep>[DELIMITER_START](?P<result>.*?)[DELIMITER_STOP]</grep>
</response>
<details>
<dbms>MySQL</dbms>
<dbms_version>>= 5.0</dbms_version>
</details>
</test>
1. title屬性
title屬性為當前測試Payload的標題,通過標題就可以了解當前的註入手法與測試的數據庫類型。
2. stype屬性
這一個屬性標記著當前的註入手法類型,1為布爾類型盲註,2為報錯註入。
3. level屬性
這個屬性是每個test都有的,他是作用是是限定在SQL測試中處於哪個深度,簡單的來說就是當你在使用SQLMAP進行SQL註入測試的時候,需要指定掃描的level,默認是1,最大為5,當level約高是,所執行的test越多,如果你是指定了level5進行註入測試,那麼估計執行的測試手法會將超過1000個。
4. clause與where屬性
test中的clause與where屬性與boundary中的clause與where屬性功能是相同的。
5. payload屬性
這一屬性既是將要進行測試的SQL語句,也是SQLMap掃描邏輯的關鍵,其中的[RANDNUM],[DELIMITER_START],[DELIMITER_STOP]分別代表著隨機數值與字符。當SQLMap掃描時會把對應的隨機數替換掉,然後再與boundary的前綴與後綴拼接起來,最終成為測試的Payload。
6. details屬性
其子節點會一般有兩個,其dbms子節所代表的是當前Payload所適用的數據庫類型,當前例子中的值為MySQL,則表示其Payload適用的數據庫為MySQL,其dbms_version子節所代表的適用的數據庫版本。
7. response屬性
這一屬性下的子節點標記著當前測試的Payload測試手法。
grep :報錯註入
comparison :布爾類型忙註入
time :延時註入
char :聯合查詢註入
SQLMAP當中的checkSqlInjection函數即是用這一屬性作為判斷依據來進入不同的處理分支。而且其中response屬性中的值則為其SQL註入判斷依據,就如當前的例子中,grep中的值為[DELIMITER_START](?P<result>.*?)[DELIMITER_STOP],SQLMap會將[DELIMITER_START]與[DELIMITER_STOP]替換成Payload中所對應替換的值,然後利用所得到的對返回的頁面信息進行正則匹配,如果存在在判斷為當前存在SQL註入漏洞。
其中要註意的是,Payload中的字符串會根據當前Payload所適用的數據庫類型對字符串進行處理,其處理的代碼位於\plugins\dbms下對應數據庫文件夾中的syntax.py腳本中。
所以最終的payload是根據test的payload子節點和boundary的prefix(前綴)、suffix(後綴)子節點的值組合而成的,即:最終的payload = url參數 + boundary.prefix+test.payload+boundary.suffix
三、實例
接下來以報錯註入來實際講解下Payload與boundary的使用。
上例子中的boundary元素中的where節點的值為1,2,含有test元素的where節點的值(1),並且,boundary元素中的clause節點的值為1,含有test元素的where節點的值(1),因此,該boundary和test元素以匹配。test元素的payload的值為:
AND (SELECT [RANDNUM] FROM(SELECT COUNT(*),CONCAT(‘[DELIMITER_START]‘,(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN 1 ELSE 0 END)),‘[DELIMITER_STOP]‘,FLOOR(RAND(0)*2))x FROM information_schema.tables GROUP BY x)a)
之前已經介紹了最終的Payload是如何的一個格式,所以最後將其中的[RANDNUM]、[DELIMITER_START]、[DELIMITER_STOP]替換掉與轉義之後。
則生成的payload類似如下:
[RANDNUM] = 2214
[DELIMITER_START] = ~!(轉義後則為0x7e21)
[DELIMITER_STOP] = !~(轉義後則為0x217e)
Payload: ‘ AND (SELECT 2214 FROM(SELECT COUNT(*),CONCAT(0x7e21,(SELECT (CASE WHEN (2214=2214) THEN 1 ELSE 0 END)),0x217e,FLOOR(RAND(0)*2))x FROM information_schema.tables GROUP BY x)a) AND ‘pujM‘=‘pujM
如果http://127.0.0.1/search-result.php?keyword=&ad_id=3存在註入的話,那麽執行的時候就會報如下錯誤:
Duplicate entry ‘~!1!~1‘ for key ‘group_key‘
根據之前的講解,那麽最終於測試的URL如下:
http://127.0.0.1/search-result.php?keyword=&ad_id=‘ AND (SELECT 2214 FROM(SELECT COUNT(*),CONCAT(0x7e21,(SELECT (ELT(2214=2214,1))),0x217e,FLOOR(RAND(0)*2))x FROM INFORMATION_SCHEMA.CHARACTER_SETS GROUP BY x)a) AND ‘YmRM‘=‘YmRM
如下為返回的頁面信息:
最後根據grep中的正規來匹配當前頁面。
<grep>[DELIMITER_START](?P<result>.*?)[DELIMITER_STOP]</grep>
而使用正則:~!(?P<result>.*?)!~來匹配Duplicate entry ‘~!1!~1‘ for key ‘group_key‘ 的結果為1,根據匹配的結果可以得出當前的頁面確實存在著SQL註入。
總結
通過SQLMap的掃描邏輯,我們可以了解到SQL註入的常規手法與實現,熟悉SQLMap的配置文件之後,自己就可以根據實際的情況對Payload與boundary進行修改,通過增加Payload與boundary來增強SQLMap的掃描規則,也可以利用其掃描規則來打造一款自己的SQL掃描工具。
SQLMap的前世今生(Part1)