作者:
T-Safe
·
2016/04/11 9:50
0x00 疑問
一直在用mysql資料庫報錯注入方法,但為何會報錯?
百度谷歌知乎了一番,發現大家都是把官網的結論發一下截圖,然後執行sql語句證明一下結論,但是沒有人去深入研究為什麼rand不能和order by一起使用,也沒徹底說明三者同時使用報錯的原理。
0x01 位置問題?
select count(*),(floor(rand(0)*2))x from information_schema.tables group by x;
這是網上最常見的語句,目前位置看到的網上sql注入教程,floor
都是直接放count(*)
後面,為了排除干擾,我們直接對比了兩個報錯語句,如下圖
由上面的圖片,可以知道報錯跟位置無關。
0x02 絕對報錯還是相對報錯?
是不是報錯語句有了floor(rand(0)*2)
以及其他幾個條件就一定報錯?其實並不是如此,我們先建建個表,新增一條記錄看看,如下圖:
確認表中只有一條記錄後,再執行報錯語句看看,如下圖:
多次執行均未發現報錯。
然後我們新增一條記錄。
然後再測試下報錯語句
多次執行並沒有報錯
OK 那我們再增加一條
執行報錯語句
ok 成功報錯
由此可證明floor(rand(0)*2)
報錯是有條件的,記錄必須3條以上,而且在3條以上必定報錯,到底為何?請繼續往下看。
0x03 隨機因子具有決定權麼(rand()和rand(0))
為了更徹底的說明報錯原因,直接把隨機因子去掉,再來一遍看看,先看一條記錄的時候,如下圖:
一條記錄的話 無論執行多少次也不報錯
然後增加一條記錄。
兩條記錄的話 結果就變成不確定性了
隨機出現報錯。
然後再插入一條
三條記錄之後,也和2條記錄一樣進行隨機報錯。
由此可見報錯和隨機因子是有關聯的,但有什麼關聯呢,為什麼直接使用rand()
,有兩條記錄的情況下就會報錯,而且是有時候報錯,有時候不報錯,而rand(0)
的時候在兩條的時候不報錯,在三條以上就絕對報錯?我們繼續往下看。
0x04 不確定性與確定性
前面說過,floor(rand(0)*2)
報錯的原理是恰恰是由於它的確定性,這到底是為什麼呢?從0x03我們大致可以猜想到,因為floor(rand()*2)
不加隨機因子的時候是隨機出錯的,而在3條記錄以上用floor(rand(0)*2)
就一定報錯,由此可猜想floor(rand()*2)
是比較隨機的,不具備確定性因素,而floor(rand(0)*2)
具備某方面的確定性。
為了證明我們猜想,分別對floor(rand()*2)
和floor(rand(0)*2)
在多記錄表中執行多次(記錄選擇10條以上),在有12條記錄表中執行結果如下圖:
連續3次查詢,毫無規則,接下來看看select floor(rand(0)*2) from `T-Safe`;
,如下圖:
可以看到floor(rand(0)*2)
是有規律的,而且是固定的,這個就是上面提到的由於是確定性才導致的報錯,那為何會報錯呢,我們接著往下看。
0x05 count與group by的虛擬表
使用select count(*) from `T-Safe` group by x;
這種語句的時候我們經常可以看到下面類似的結果:
可以看出 test12的記錄有5條
與count(*)
的結果相符合,那麼mysql在遇到select count(*) from TSafe group by x;
這語句的時候到底做了哪些操作呢,我們果斷猜測mysql遇到該語句時會建立一個虛擬表(實際上就是會建立虛擬表),那整個工作流程就會如下圖所示:
- 先建立虛擬表,如下圖(其中key是主鍵,不可重複):
2.開始查詢資料,取資料庫資料,然後檢視虛擬表存在不,不存在則插入新記錄,存在則count(*)
欄位直接加1,如下圖:
由此看到 如果key存在的話就+1, 不存在的話就新建一個key。
那這個和報錯有啥內在聯絡,我們直接往下來,其實到這裡,結合前面的內容大家也能猜個一二了。
0x06 floor(rand(0)*2)報錯
其實mysql官方有給過提示,就是查詢的時候如果使用rand()
的話,該值會被計算多次,那這個“被計算多次”到底是什麼意思,就是在使用group by
的時候,floor(rand(0)*2)
會被執行一次,如果虛表不存在記錄,插入虛表的時候會再被執行一次,我們來看下floor(rand(0)*2)
報錯的過程就知道了,從0x04可以看到在一次多記錄的查詢過程中floor(rand(0)*2)
的值是定性的,為011011…(記住這個順序很重要),報錯實際上就是floor(rand(0)*2)
被計算多次導致的,具體看看select count(*) from TSafe group by floor(rand(0)*2);
的查詢過程:
1.查詢前預設會建立空虛擬表如下圖:
2.取第一條記錄,執行floor(rand(0)*2)
,發現結果為0(第一次計算),查詢虛擬表,發現0的鍵值不存在,則floor(rand(0)*2)
會被再計算一次,結果為1(第二次計算),插入虛表,這時第一條記錄查詢完畢,如下圖:
3.查詢第二條記錄,再次計算floor(rand(0)*2)
,發現結果為1(第三次計算),查詢虛表,發現1的鍵值存在,所以floor(rand(0)*2)
不會被計算第二次,直接count(*)
加1,第二條記錄查詢完畢,結果如下:
4.查詢第三條記錄,再次計算floor(rand(0)*2)
,發現結果為0(第4次計算),查詢虛表,發現鍵值沒有0,則資料庫嘗試插入一條新的資料,在插入資料時floor(rand(0)*2)
被再次計算,作為虛表的主鍵,其值為1(第5次計算),然而1這個主鍵已經存在於虛擬表中,而新計算的值也為1(主鍵鍵值必須唯一),所以插入的時候就直接報錯了。
5.整個查詢過程floor(rand(0)*2)
被計算了5次,查詢原資料表3次,所以這就是為什麼資料表中需要3條資料,使用該語句才會報錯的原因。
0x07 floor(rand()*2)報錯
由0x05我們可以同樣推理出不加入隨機因子的情況,由於沒加入隨機因子,所以floor(rand()*2)
是不可測的,因此在兩條資料的時候,只要出現下面情況,即可報錯,如下圖:
最重要的是前面幾條記錄查詢後不能讓虛表存在0,1鍵值,如果存在了,那無論多少條記錄,也都沒辦法報錯,因為floor(rand()*2)
不會再被計算做為虛表的鍵值,這也就是為什麼不加隨機因子有時候會報錯,有時候不會報錯的原因。如圖:
當前面記錄讓虛表長成這樣子後,由於不管查詢多少條記錄,floor(rand()*2)
的值在虛表中都能找到,所以不會被再次計算,只是簡單的增加count(*)
欄位的數量,所以不會報錯,比如floor(rand(1)*2)
,如圖:
在前兩條記錄查詢後,虛擬表已經存在0和1兩個鍵值了,所以後面再怎麼弄還是不會報錯。
總之報錯需要count(*)
,rand()
、group by
,三者缺一不可。
本文章來源於烏雲知識庫,此映象為了方便大家學習研究,文章版權歸烏雲知識庫!