對寬字節注入的漏洞實現淺析和如何簡單修復
原創【51CTO.com原創稿件】寬字節注入之前我看到過,但是沒有實戰過,后面也沒有找到合適的測試環境,今天剛好看到一個關于寬字節注入的ctf題,因此借此來學習下寬字節注入,如果寫得不好的地方,煩請各位多多指導!
本文主要是簡單介紹下寬字節注入,以及如何通過手工和工具進行寬字節注入的一個利用,通過本文主要可學習到以下三點:
- 擴展了對SQL注入進行探測的一個思路!
- 學習了如何使用寬字節探測注入!
- 如何使用sqlmap自動化對寬字節進行注入!
1. 寬字節注入
這里的寬字節注入是利用MySQL的一個特性,MySQL在使用GBK編碼(GBK就是常說的寬字節之一,實際上只有兩字節)的時候,會認為兩個字符是一個漢字(前一個ascii碼要大于128,才到漢字的范圍),而當我們輸入有單引號時會自動加入\進行轉義而變為\’(在PHP配置文件中magic_quotes_gpc=On的情況下或者使用addslashes函數,icov函數,mysql_real_escape_string函數、mysql_escape_string函數等,提交的參數中如果帶有單引號’,就會被自動轉義\’,使得多數注入攻擊無效),由于寬字節帶來的安全問題主要是吃ASCII字符(一字節)的現象,將后面的一個字節與前一個大于128的ascii碼進行組合成為一個完整的字符(MySQL判斷一個字符是不是漢字,首先兩個字符是一個漢字,另外根據gbk編碼,第一個字節ascii碼大于128,基本上就可以了),此時’前的\就被吃了,我們就可以使用’了,利用這個特性從而可實施SQL注入的利用。
最常使用的寬字節注入是利用%df,其實只要第一個ascii碼大于128就可以了,比如ascii碼為129的就可以,但是我們怎么將他轉換為URL編碼呢?其實很簡單,我們先將129(十進制)轉換為十六進制,為0x81,如圖1所示,然后在十六進制前面加%即可,即為%81,任意進制在線轉換網站請點擊此處!另外可以直接記住GBK首字節對應0×81-0xFE,尾字節對應0×40-0xFE(除0×7F),則尾字節會被吃點,如轉義符號\對應的編碼0×5C!另外簡單提一下,GB2312是被GBK兼容的,它的高位范圍是0xA1-0xF7,低位范圍是0xA1-0xFE(0x5C不在該范圍內),因此不能使用編碼吃掉%5c。
圖1 將十進制129轉換為十六進制
2. 實驗環境
本次實驗環境如下:
URL地址:http://103.238.227.13:10083/
相關版本信息:
- PHP 7.0.7、mysql 5.5.48-log!
- WebServer: nginx!
- Content-Type: text/html; charset=gbk!
以上多數信息可使用F12進行簡單的信息收集(近期我正在將我目前會的滲透實戰中常用的信息收集進行整理,其中第一個信息收集就是F12信息收集),通過F12我們收集到如圖2所示信息,另外我們也可通過添加火狐插件server-spy進行這些信息的獲取。
圖2 F12信息收集
3. SQL注入簡單思路
今天學習到了一個SQL注入探測的一個簡單思路,而不是說就簡單的and或者or以及’進行探測,或者直接使用sqlmap的-b參數來進行探測(以前的我是這樣的),因此可能錯過了好幾個億。對SQL注入進行探測可根據以下思路來進行:
第一步: 簡單使用and、or、’,判斷是否有注入,不行接著加入一些自創的語句進行簡單判斷(如order by看有沒有報錯等)!
第二步,如果第一步沒有看到效果,可進行寬字節注入探測,輸入%bf'(或者%81’),發現報錯,存在寬字節注入!
第三步,如果上述兩步都沒有效果,可以接著但不限于http頭注入探測(sqlmap的話使用—level參數進行設置),等等。
4. 注入類型探測
SQL注入根據不同的標準有不同的分類,如根據參數類型可分為數值型注入、字符型注入,根據注入的位置可分為POST注入、GET注入、cookie注入等,而sqlmap則將注入分為基于布爾的盲注、基于時間的盲注、基于報錯的注入、聯合查詢注入、堆查詢注入,這里為了方便我將SQL分為寬字節注入和非寬字節注入(個人分類,沒有依據)。
寬字節注入在實際滲透測試中也是存在的,只是很少很少,這次剛好看到一個寬字節注入的ctf,也順便再認真學習寫寬字節注入的一些基本知識。如圖3,通過提示我們可知該題是一道字符型的注入。
圖3 注入測試提示
既然是字符型的注入,那么我們可以使用’和and來進行簡單的判斷,該測試點是否是SQL注入點!通過使用’,我們無法判斷是否存在注入,沒有可供我們判斷的信息,如圖4所示。
圖4 ‘無法判斷是否是注入
我們結合and來進一步判斷,但是依舊沒有任何信息可供我們進行判斷該測試點是否是注入點,如圖5所示。
圖5 and也無法判斷是否是注入點
難道是不存注入(肯定不是這樣的),那我們使用神器sqlmap盲跑試試,但是很失望,居然沒有直接告訴我們有沒有存在注入點,如圖6所示。
圖6 sqlmap提示不像是注入
如果按照我以前的思路,我可能就已經放棄了,在實戰中的話可能就認為真的不是注入點了(有點草率),此時我們使用思路的第二步來進行判斷,該測試點是不是寬字節注入,我們使用ascii碼的129(也即是%81,可以使用常使用的%df)進行探測,如圖7所示,發現頁面報錯了,根據頁面信息可判斷該測試點存在SQL注入,并且是寬字節注入。
圖7 判斷注入是寬字節注入
5. 寬字節手工簡單注入
通過上面的說明,我們可知該注入點是寬字節注入,且是字符型的注入,此時我們可通過構造SQL語句來進行SQL的寬字節注入,另外我們也可測試下我們輸入的’是否被轉義成為了\’,如圖8所示,我們輸入的’確實被轉義了!
圖8 ‘被轉義了
此時我們可構造語句%81’ and 1=1-- -來進行注入,原因是’被轉義成為\’,而%81的ascii碼大于128,此時%81’就被看作一個字符(兩個字節),此時’就可以使用了,而后面我們構造一個數值型的注入語句,最后再加注釋把最后的那個’注釋掉,如圖9所示。
圖9 寬字節注入
此時我們使用order by來看看當前數據庫有幾列,發現數據庫有三列,如圖10所示:
圖10 當前數據庫有3列
我們看看我們將要查詢的數據會在頁面中哪些地方顯示,如圖11所示。
圖11 查詢在頁面中顯示的位置
到此,手工注入就結束了,后續操作請查看我的文章《mysql手工注入》的最后部分!
6. 寬字節sqlmap注入
在前面我們直接使用sqlmap盲跑,是沒有發現該測試點是SQL注入點的,根據上面的特性,我們可以在id后面添加一個%81’,然后在使用sqlmap進行注入,注入語句為:
- sqlmap.py -u "http://103.238.227.13:10083/?id=1%81’" -b
這時,我們發現sqlmap可成功發現該注入點了,如圖12所示。
圖12 sqlmap成功檢測出該注入點
除了上面的這種方法外,我們可以使用寬字節注入腳本來使用sqlmap進行注入,注入語句為:
- sqlmap.py -u "http://103.238.227.13:10083/?id=1" -b --tamper=unmagicquotes.py
這樣我們也能成功探測出該注入點,如圖13所示,對于后續的注入此處不再累贅!
圖13 sqlmap成功檢測出注入點
7. 簡單修復建議
在初始化連接和字符集之后,使用SET character_set_client=binary來設定客戶端的字符集是二進制的,另外對于服務器端返回的錯誤信息要么進行自定義,要么進行歸一化的錯誤提示!
【51CTO原創稿件,合作站點轉載請注明原文作者和出處為51CTO.com】