剛開始學習sql注入,遇見了 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,不存在則新建該分組。
報錯分析
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
的虛擬表,最終引起了報錯。