sql注入--雙查詢報錯注入原理探索



雙查詢報錯注入原理探索

上一篇講了雙查詢報錯查詢注入,后又參考了一些博客,今天來探究其原理

實際上該報錯是由於rand(),count(),group by 三個語句聯合使用造成的,缺一不可。
上一篇的地址:https://www.cnblogs.com/laoxiajiadeyun/p/10278512.html

part 1 場景復現


首先我們新建一個數據庫,並創建一個表用作實驗:

mysql> CREATE DATABASE sql_test;
mysql> USE sql_test;
mysql> CREATE TABLE test(id int(2),name varchar(10));

1547704744034

接下來先插入一條數據測試:

mysql> INSERT INTO test VALUES("1","aaa");

下面看見已經插入了一條數據:

1547705572218

接下來我們構造一個報錯條件,讓其報錯,顯示出當前數據庫名:

mysql> SELECT count(*),concat((SELECT database()),"~",floor(rand()*2))as a FROM test GROUP BY a;

查詢的結果要么為sql_test0,要么是sql_test1,取決於隨機數取整結果,不會觸發報錯。

1547705814418

1547705838535

接下來再在表中插入一條數據進行測試:

mysql> INSERT INTO test VALUES("2","bbb");

1547706027911

雙查詢語句與之前一樣

運氣很好,第一次就報錯了,錯誤內容意思:group by 操作時主鍵 ‘sql_test~1’ 重復

1547706189543

還有其他正常執行不報錯的情況:

1547706399949

可以看到,這里存在兩條數據就可以引發報錯,得到數據庫信息。

part 2 形成原因


接下來我們再分析其報錯的形成 原因:

先談group by 函數:

在表中再插入兩條數據,name值都為“bbb”:

mysql> INSERT INTO test VALUES("3","bbb");
mysql> INSERT INTO test VALUES("4","bbb");

成功后表如下:

1547706801693

這時候我們使用group by 語句時,MySQL會將查詢結果分類匯總,重復的內容會合並為一項:

mysql> SELECT name FROM test GROUP BY name;

1547707005212

這時候再使用count()函數就可以對不同的條目計數:

mysql> SELECT count(*),name FROM test GROUP BY name;

如圖:aaa有一條,bbb有3條

1547707132342

其背后的實現原理如下:

在執行group by name語句時,MySQL會在內部建立一個虛擬表,用來儲存列的數據,表中會有一個group_key值作為表的主鍵,這里的主鍵就是用來分類的name列中獲取,當查詢數據時,取數據庫數據,然后查看虛擬表中存在不,不存在則插入新記錄

1547707392629

當讀取到第一行數據時,aaa不存在,將aaa放入主鍵列中,1放在id列中

1547707587564

然后繼續往下走,到了bbb,不存在,也放進去

1547707641420

往下執行,遇到多余的bbb,已經有bbb存在,就匯總在一起,內部情況如下:

1547707746563

如下,最后在查詢的時候根據group by內部的實現方式返回分類后的結果:

1547708050507

當我們加上count(*)函數時,操作過程為:查看虛擬表是否存在該主鍵值,不存在則插入新記錄,存在則count(*)字段直接加1

這樣就能對上面的分類結果進行統計,然后將統計結果返回:

1547708171151

所以雙查詢報錯的關鍵就在這里,主要的原因在於rand()函數在group by的過程中被觸發了多次

part 3 報錯原理


讓我們回看一下構造的報錯語句:

mysql> SELECT count(*),concat((SELECT database()),"~",floor(rand()*2))as a FROM test GROUP BY a;

執行前虛擬表為空:

1547708989795

當第一次執行時,group by 分組,其取的數據的是以a為別名的這條語句,假設這時的concat((SELECT database()),"~",floor(rand()*2))生成結果為sql_test~0,group就以sql_test~0查詢虛擬表,發現表中沒有該值的主鍵,於是將這條語句的結果插入到虛擬表中。

注意!是將這條語句的結果插入到虛擬表中,而不是將 sql_test~0 插入到虛擬表中,如下:

(將concat((SELECT database()),"~",floor(rand()*2)) 以a為別名,方便作圖)

1547710722191

由於虛擬表沒有內容,所以會將其插入到虛擬表中,這里的插入過程中,由於插入的是a語句的結果,所以在插入時a語句中的rand()函數會再次執行,即插入的值可能為sql_test~0 也可能為 sql_test~1 ,這里假設插入時a執行的結果為sql_test~0

1547711441161

所以上面的情況就是用sql_test~1這個結果查詢虛擬表,不存在該數據,於是插入虛擬表,插入時又運算一次,然后插入的值變成了sql_test~0,所以這就是主要的沖突,表中只有一條數據還好,即使查詢虛擬表的值和插入虛擬表的值不是同一個,但虛擬表也只生成一條記錄,不會出現問題。

然而當表的數據出現兩條以上的時候,第group by 在處理完第一條數據后會往下繼續處理第二條,於是第二條還會按第一條的處理方式進行:

1547712230675

於是就會報錯,報錯內容如下:

ERROR 1062 (23000): Duplicate entry 'sql_test~0' for key 'group_key'

1547712349931

如果第二次查詢和插入的結果都一致:就會有下面兩種情況:

  • 都是sql_test~0:表里已存在,該主鍵的count(*)值+1

  • 都是sql_test~1:表里沒有,插入形成新的主鍵

1547713202940

part 4 探索小結


所以成因已經明白了:當group by 在查詢虛擬表和插入虛擬表時,如果這兩次a語句執行的結果不一致就會引發錯誤,錯誤提示信息是插入的主鍵重復,通過自定義提示里報錯信息中的主鍵值來獲得敏感信息。

其中還可以通過修改rand()函數的隨機因子,指定隨機數生成方式來提高報錯的效率,具體見深入分析的參考鏈接,這里不過多贅述。

希望這篇文章能給你帶來幫助,如果文中有不正確的地方,還請私信或評論留言,我會仔細查看。

參考鏈接:


group by:https://blog.csdn.net/hao1066821456/article/details/69556644

雙查詢注入:https://www.cnblogs.com/BloodZero/p/4660971.html

http://www.lijiejie.com/mysql-injection-error-based-duplicate-entry/

Mysql報錯注入原理分析:https://www.cnblogs.com/xdans/p/5412468.html#undefined


免責聲明!

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



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