>>> a = 啊>>> axb0xa1>>> a.decode(gbk)uu554a>>> a.decode(gbk).encode(utf-8)xe5x95x8a
因為終端使用的是gbk編碼,所以 啊的編碼為xb0xa1 以gbk編碼格式進行解析,解析成unicode編碼,再使用encode編碼方式編碼成utf-8。
在這裡需要再次強調一下Python string的默認編碼方式 ascii形式,也就是說在不指定string編碼方式的情況下默認以ascii進行解析,因此會造成很多編碼問題。
比如下面兩個例子,正好將encode和decode函數都有講解到
Python 寫入字元串到文件
這裡只針對py2,
write方法的參數類型是str,str是二進位流(不包含編碼信息),當給出一個unicode對象時,會執行str函數轉換成str類型再送給write方法。unicode轉str包含一次編碼,如不指定則默認使用ascii編看下面的錯誤
# coding:utf-8a = u啊f = open(./111,a)
f.write(a)
Traceback (most recent call last):
File "F:codepython2.py", line 4, in <module>
f.write(a)
UnicodeEncodeError: ascii codec cant encode character uu554a in position 0: ordinal not in range(128)
這裡產生錯誤的原因是,unicode首先要encode轉化為ascii編碼,顯然有很多位元組是對不上號的,這裡的解決方法有兩個,其一是改變其默認編碼使用下面代碼
# coding:utf-8import sys
reload(sys)
sys.setdefaultencoding(utf-8)
a = u啊f = open(./111,a)
f.write(a)
其二將unicode轉換為其他編碼過後再次存入文件
# coding:utf-8a = u啊f = open(./111,a)
f.write(a.encode(gbk))
繞過unicode直接進行編碼轉化
首先看下下面代碼
>>> a = 啊>>> print a.encode(utf-8)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
UnicodeDecodeError: ascii codec cant decode byte 0xb0 in position 0: ordinal not in range(128)
a原本為gbk編碼,如果直接進行encode那麼首先會轉換編碼為unicode格式,因為默認解析格式為ascii所以在ascii轉換成unicode編碼時會產生以上錯誤。那麼這時可以改變默認編碼
# coding:utf-8import sys
reload(sys)
sys.setdefaultencoding(utf-8)
a = 啊print a.encode(utf-8)
成功解決該類編碼問題
終端顯示編碼
在第二小節的圖示中最後一個環節,是終端編碼的問題。
問題一般出在交給操作系統的字元編碼與終端顯示編碼有所差別。在windows cmd終端採用的gbk編碼
如果使用utf-8編碼就會出現亂碼
在linux 終端下採用的是utf-8編碼形式![enter description here](./attachments/Little CMS.md "Little CMS")如果使用gbk編碼就會出現亂碼
PHP
載入
PHP編碼相對 Python而言簡單了許多,PHP直接使用ascii進行單位元組解析,也就是說不論文件採用什麼編碼,PHP在解析是總是按位元組處理,如下面例子:
該PHP腳本採用gbk編碼方式編碼,"誠" 字的位元組碼為"D55c" 5c為使得PHP解析錯誤。
顯示
PHP以及HTML腳本可以指定顯示在瀏覽器上的編碼方式
header("content-type:text/html;charset=utf-8")<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
MYSQL
編碼過程
mysql 編碼數據比較複雜,大體上可以分為以下幾個
1、character_set_client
2、character_set_connection
3、character_set_results
4、數據欄位存儲
character_set_client
無論客戶端傳遞的是什麼編碼的數據,伺服器都當成該編碼來處理,例如該編碼為UTF8,那麼如果客戶端發送過來的數據不是UTF8,那麼就會出現亂碼;character_set_connection
connection 可以說是在SQL語句執行時的編碼 ,當執行的是查詢語句時,客戶端發送過來的數據會先轉換成connection指定的編碼。但只要客戶端發送過來的數據與client指定的編碼一致,那麼轉換就不會出現問題;
數據欄位存儲
當insert數據時需要將數據存儲在資料庫中,這裡就涉及到編碼轉化,當沒用找到對應關係時就會令該位置字元為3F ,這也是資料庫中亂碼中3F出現的原因。character_set_results
當select從資料庫中取出時以前的編碼要變為character_set_results設置的編碼。整個流程可參照下圖:
從查詢到取出整個流程經歷了三次編碼轉換,每次轉換都有可能產生編碼問題。client編碼指定了用戶輸入的編碼格式,connection按照此編碼格式將編碼轉化為connection指定的編碼,然後再將編碼轉化為存儲格式。當有查詢操作時再將取出的數據按照results格式轉化。
這裡盜用一張圖
驗證原理
設計以下實驗
實驗一 (驗證數據存儲時的轉換) 寫一個資料庫交互的代碼,在前端使用GBK編碼方式插入數據,character_set_client設置為GBK,character_set_connection設置為GBK,數據存儲設置為utf8,查看最後的存儲位元組為E8AAA0為正確的utf8編碼http://127.0.0.1/1.php?a=%d5%5c$conn->query("SET character_set_client = gbk"); $conn->query("SET character_set_connection = gbk"); ALTER TABLE `yz` CHANGE `a` `a` TEXT CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL;
實驗二(驗證client到connection的轉換)
在前端使用GBK編碼方式插入數據,character_set_client設置為GBK,character_set_connection設置為utf8,數據存儲設置為utf8,查看最後的存儲位元組為E8AAA0為正確的uft8編碼http://127.0.0.1/1.php?a=%d5%5c$conn->query("SET character_set_client = gbk"); $conn->query("SET character_set_connection = utf8"); ALTER TABLE `yz` CHANGE `a` `a` TEXT CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL;
實驗三(驗證數據存儲到取出的顯示的轉換)
將以utf8編碼存儲的內容用gbk格式取出
http://127.0.0.1/1.php?a=%d5%5c$conn->query("SET character_set_client = gbk"); $conn->query("SET character_set_connection = utf8"); $conn->query("SET character_set_results = gbk"); ALTER TABLE `yz` CHANGE `a` `a` TEXT CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL;
成功轉化為gbk格式,沒有亂碼出現enter code here
0x02 漏洞產生GBK編碼新問題
讓我們回顧一下GBK寬位元組注入的相關細節在可以使用單引號的前提下,使用addslashes將單引號轉義,又因為database使用的是GBK編碼,所以如果前面有%d5等位元組存在,就會將反斜杠吃掉變為%d5%5c在gbk編碼裡面這是一個合法字元所以只剩下單引號,成功逃逸。
在研究編碼問題的時候發現PHP是按照位元組處理的所以如果對於%d5%5c這樣的gbk編碼如果使用addslashes的話會變成%d5%5c%5c,這樣在SQL server處理的時候如果以gbk格式進行數據查詢就會 多出一個反斜杠 ,利用該反斜杠可以轉移其他特殊字元,具體使用方法如下:
源碼鏈接
$conn->query("SET NAMES gbk"); sql_get("select * from yz where b=$a and c=$b");
如果$a和$b兩個參數分別傳入
a=%d5%5c&b=or 1 %23
經過編碼解析成為
select * from yz where b=誠 and c=or 1 #
成功進行注入
0x03 總結
系統的總結了在以前學習過程中遇到的編碼問題,學習了編碼問題產生的原因以及糾錯方法,同時在研究過程中找到了新的漏洞所在。那麼總結來看編碼問題還是三大點
1、以錯誤的方式解析字元編碼
2、在編碼轉化時被轉化的編碼沒有相應的值
3、在編碼轉化時沒有相應的值與被轉化的編碼對應
上圖中A是UTF8編碼,B是GBK編碼,簡單來講上述問題可以這麼描述
1、A字元串用GBK編碼進行解析,必然會產生編碼錯誤
2、現在A要轉化為B種編碼,但在解析A時默認編碼為ascii,從而出現解析錯誤
3、現在A要轉化為B種編碼,以UTF-8格式解析A,但在轉化成GBK編碼時發現沒有對應的字元編碼,這裡就出現了轉換錯誤
簡單的總結到這裡,如果後面遇到了其他問題會及時補充,如果描述有錯請及時指正。
0x04 參考鏈接
mysql編碼層次介紹
GBK編碼
(註:本文屬於合天原創投稿獎勵,未經允許,禁止轉載!) 推薦閱讀: