sql注入--雙查詢報錯注入


sql注入--雙查詢報錯注入

背景:在sqli-labs第五關時,即使sql語句構造成功頁面也沒有回顯出我們需要的信息,看到了有使用雙查詢操作造成報錯的方式獲得數據庫信息,於是研究了一下雙查詢的報錯原理,總結了探索的過程,整理出此文希望可以幫到感興趣的人。

sqli-labs闖關游戲下載地址:https://github.com/Audi-1/sqli-labs

雙查詢報錯注入

需用到四個函數和一個group by語句:

  1. group by ... --->分組語句 //將查詢的結果分類匯總

  2. rand() --->隨機數生成函數

  3. floor() --->取整函數 //用來對生成的隨機數取整

  4. concat() --->連接字符串

  5. count() --->統計函數 //結合group by語句統計分組后的數據

用sqli-labs中的數據庫為示例,首先先了解一下子查詢的概念。

子查詢又稱為內部查詢,子查詢允許把一個查詢嵌套在另一個查詢當中

簡單的來說就是一個select中又嵌套了一個select,嵌套的這個select語句就是一個子查詢。

連接數據庫后用子查詢測試一下:

mysql> SELECT concat("test:",(SELECT database()))as a;

1547625960641

執行查詢操作時,子查詢先開始,所以SELECT database()先執行,然后查詢到當前數據庫名稱”security“,並將其傳給concat函數,concat函數在對字符進行連接,於是顯示出圖上的結果。

然后是rand()函數,其作用是生成一個大於0小於1的隨機浮點數,如下:

1547626516690

floor()函數的作用是對傳入的參數取整,這里將rand()生成的隨機數做處理進行取整,由於rand()生成的值取整結果只能為0,所以我們這里做一點處理,使其生成一個大於0小於2的隨機值,並對其取整:

mysql> SELECT floor(rand()*2);

結果要么為0要么為1

1547626840990

接下來結合子查詢,顯示出數據庫信息:

mysql> SELECT concat((SELECT database()), floor(rand()*2)) from users;

由於users表中只有13條數據,所以這里返回了13條數據

1547627207626

在注入中,我們不知道庫名表名,往往借助information_schema這個庫進行猜解

其中information_schema.schemata中包含了mysql的所有庫名,information_schema.tables中包含了所有的表名,information_schema.columns中包含了所有的列名。

示例如下:(我電腦中有7個數據庫,所以返回了7條數據)

1547627878261

現在加上group by語句對返回的數據進行分組處理

mysql> SELECT concat((SELECT database()), floor(rand()*2))as a from information_schema.schemata group by a;

from前的as a,是為concat((SELECT database()), floor(rand()*2))這一串取了個別名,后面使用group by分組時就不用打那么長一串了,直接使用別名就行。

1547628300028

到這里都是基礎知識的鋪墊,而且前面所有的查詢操作都是返回庫名和“0、1”的拼接結果,然而在sqli-labs第五關這樣網頁無回顯的環境下,我們是看不到任何的信息的,所以接下來才是正題,我們要利用count函數和上面的操作構成mysql內部錯誤,然后通過報錯的提示獲得我們想要的信息。

(上面的database()函數在實際注入中也可以換成其他的,如version(),具體看你想要通過報錯獲得的信息)

這里增加一個聚合函數count,構造的語句如下:

mysql> SELECT count(*),concat((SELECT database()), floor(rand()*2))as a from information_schema.schemata group by a;

這里利用count(*)對前面的返回數據進行統計,由於group by 和隨機數的原因,有可能會出現重復的鍵值,當鍵值重復時就會觸發錯誤,然后報錯,由於子查詢在錯誤發生之前就已經完成,所以子查詢的內容會隨着報錯信息一起顯示出來:

1547629499749

這里我使用的是information_schema中的schemata表,因為我的數據庫有7個,生成的隨機結果中0和1有一定比例,不容易出現全是0或者全是1的情況,實際情況下推薦使用information_schema中的tables或者columns兩個表,里面的數據條目較多,容易生成較多的隨機值。

例如:

mysql> SELECT count(*), concat((SELECT database()), floor(rand()*2))as a from information_schema.tables group by a;

在sqli-labs闖關的第五關中payload如下:

XXX.php/?id=-1' union select 1,count(*), concat((select database()), floor(rand()*2))as a from information_schema.tables group by a --+

1547630039722

1547630106105

注意,由於有隨機性,可能成功執行了語句所以不會報錯,正常的顯示頁面(即不報錯)如下:

1547630292578

這種情況多提交幾次就行,理論上每次都有百分之50的可能性

但可以通過修改rand()使用的種子來使其百分百報錯,如下將rand()改為rand(1),測試百分之百報錯:

XXX.php/?id=-1' union select 1,count(*), concat((select database()), floor(rand(1)*2))as a from information_schema.tables group by a --+

注入原理

以下是學習過程中看到的不同作者對該問題原因的解釋:

這個是最初看到的原理,但是個人覺得闡述的不太正確
當在一個聚合函數后面(比如count)后面使用分組語句,就會把查詢的一部分以錯誤形式顯示出來;因為concat函數執行兩次,比如select database(),這樣就執行了兩次select database,與后面的隨機函數鏈接在一起,可能會隨機重復,就會報錯;

另一個博客中提出的深層次的原因,比較合理:
通過floor報錯的方法來爆數據的本質是group by語句的報錯。group by語句報錯的原因是floor(random(0)*2)的不確定性,即可能為0也可能為1,(group by key的原理是循環讀取數據的每一行,將結果保存於臨時表中。讀取每一行的key時,如果key存在於臨時表中,則不在臨時表中更新臨時表中的數據;如果該key不存在於臨時表中,則在臨時表中插入key所在行的數據。group by floor(random(0)*2)出錯的原因是key是個隨機數,檢測臨時表中key是否存在時計算了一下floor(random(0)*2)可能為0,如果此時臨時表只有key為1的行和不存在key為0的行,那么數據庫要將該條記錄插入臨時表,由於是隨機數,插時又要計算一下隨機值,此時floor(random(0)*2)結果可能為1,就會導致插入時沖突而報錯。即檢測時和插入時兩次計算了隨機數的值。

結論是:當與臨時表里面的值進行比較,如果不同,就插入,但是插入的時候又計算了一次,所以如果插入時計算的值與直接比較的值不一樣,則報錯!

但是上述兩個理由我看了感覺還是有一些地方不明白,感覺沒有說到地方,所以又自己探索了一番,這一篇文章篇幅已經很長了,所以留在下一篇里單獨探討吧。

下一篇:雙查詢報錯注入原理探索

本文參考鏈接:


初步了解雙查詢注入:

https://www.2cto.com/article/201303/192718.html

深入理解:

https://www.cnblogs.com/BloodZero/p/4660971.html

rand()的隨機數種子的影響:

https://segmentfault.com/q/1010000000609508


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM