寬字節注入原理


0x00 前言

在mysql中,用於轉義的函數有addslashes,mysql_real_escape_string,mysql_escape_string等,還有一種情況是magic_quote_gpc,不過高版本的PHP將去除這個特性。

首先,寬字節注入與HTML頁面編碼是無關的,筆者曾經看到<metacharset=utf8>

就放棄了嘗試,這是一個誤區,SQL注入不是XSS。雖然他們中編碼的成因相似,不過發生的地點不同。

很多網上的材料都說程序使用了寬字節來處理程序,卻又不指出具體是指什么程序。本文就介紹一下具體漏洞發生的原理與簡單的利用。

 

0x01 涉及的一些概念

1. 字符、字符集與字符序

 字符(character)是組成字符集(character set)的基本單位。對字符賦予一個數值(encoding)來確定這個字符在該字符集中的位置。

字符序(collation)指同一字符集內字符間的比較規則。

2. UTF-8

由於ASCII表示的字符只有128個,因此網絡世界的規范是使用UNICODE編碼,但是用ASCII表示的字符使用UNICODE並不高效。因此出現了中間格式字符集,被稱為通用轉換格式,及UTF(UniversalTransformation Format)。

3. 寬字節

GB2312、GBK、GB18030、BIG5、Shift_JIS等這些都是常說的寬字節,實際上只有兩字節。寬字節帶來的安全問題主要是吃ASCII字符(一字節)的現象。

4.客戶端與服務器交互數據傳輸的字符集

存儲時的字符集已經確定了,不會影響交互階段的字符集。

 

在MYSQL中,還有一個中間層的結構,負責客戶端和服務器之間的連接,所以稱為連接層。

 

交互的過程如下:

 

(1)客戶端以某種字符集生成的SQL語句發送至服務器端,這個“某種字符集”其實是任意規定的,PHP作為客戶端連接MYSQL時,這個字符集就是PHP文件默認的編碼。

 

(2)服務器會將這個SQL語句轉為連接層的字符集。問題在於MYSQL是怎么知道我們傳過來的這個SQL語句是什么編碼呢?這時主要依靠兩個MYSQL的內部變量來表示,一個是character_set_client(客戶端的字符集)和character_set_connection(連接層的字符集)。可以使用show variables like ‘character_set_%’ ;進行查看。

 

 

 

可以看到,這里的客戶端字符集為GBK,連接層字符集也是為GBK。

兩者相同,就不會有問題,如果不一致,就會出現亂碼問題了。

使用MYSQL中的set命令可以對這些內部變量做設置,如修改客戶端編碼為UTF-8;

set character_set_client = UTF-8

(1)服務器將轉換好的SQL語句,轉為服務器內部編碼與存儲在服務器上的數據進行交互

(2)服務器處理完之后,將結果返回給客戶端,還是轉為服務器認為客戶端可以認識的編碼,如上圖的GBK,使用character_set_results來確定返回客戶端的編碼。

平時在PHP中寫的set names UTF-8相當於下面三條同時執行:

(1)set character_set_client = UTF-8

(2)set character_set_connection = UTF-8

(3)set character_set_results = UTF-8

 

5、亂碼問題原理

設置三個字符集相同,這也就不會出現亂碼的真正原理。網頁上有時會出現亂碼是因為PHP動態文件將數據打印到瀏覽器的時候,瀏覽器也會按照一定的字符集進行判斷,如果PHP的響應數據編碼和瀏覽器編碼一致,就不會出現亂碼,否則就出現亂碼。可以通過在PHP中使用header()來指定這個響應數據的編碼。

 

0x02 mysql字符集轉換過程

1. MySQL Server收到請求時將請求數據從character_set_client轉換為character_set_connection;

2. 進行內部操作前將請求數據從character_set_connection轉換為內部操作字符集,其確定方法如下:

• 使用每個數據字段的CHARACTER SET設定值;

• 若上述值不存在,則使用對應數據表的DEFAULT CHARACTER SET設定值(MySQL擴展,非SQL標准);

• 若上述值不存在,則使用對應數據庫的DEFAULT CHARACTER SET設定值;

• 若上述值不存在,則使用character_set_server設定值。
將操作結果從內部操作字符集轉換為character_set_results。
重點:寬字節注入發生的位置就是PHP發送請求到MYSQL時字符集使用character_set_client設置值進行了一次編碼。

0x03 案例演示

有三種形式

情景一:

在PHP中使用mysql_query(“set names GBK”);指定三個字符集(客戶端、連接層、結果集)都是GBK編碼。

情景代碼:

1     .....
2     mysql_query(“set names GBK”);
3     $bar = addslashes($_GET[‘bar’]) ;
4     $sql = “select password from user where bar=’{$bar}’”;
5     $res = mysql_query($sql) ;
6     ......

提交:http://127.0.0.1/foo.php?bar=admin%df%27

這時,發生如下轉換:

%df%27=====(addslashes)======>%df%5c%27======(GBK)======>運’

帶入sql為:

Select password from user where bar=‘運’

成功將單引號閉合。為了避免漏洞,網站一般會設置UTF-8編碼,然后進行轉義過濾。但是由於一些不經意的字符集轉換,又會導致漏洞。

 

情景二:

使用set names UTF-8指定了UTF-8字符集,並且也使用轉義函數進行轉義。有時候,為了避免亂碼,會將一些用戶提交的GBK字符使用iconv函數(或者mb_convert_encoding)先轉為UTF-8,然后再拼接入SQL語句。

情景代碼:

1     ....
2     mysql_query(“set names UTF-8”) ;
3     $bar =iconv(“GBK”,”UTF-8”, addslashes($_GET[‘’bar])) ;
4     $sql = “select password from user where bar=’{$bar}’” ;
5     $res = mysql_query($sql) ;
6     ......

我們可以看到,為了使得SQL語句中的字符集保持一致,一般都會使用iconv等字符集轉換函數進行字符集轉換,問題就是出在了GBK向UTF-8轉換的過程中。

提交:http://127.0.0.1/foo.php?bar=%e5%5c%27

變換過程:(e55c轉為UTF-8為e98ca6)

e55c27====(addslashes)====>e55c5c5c27====(iconv)====>e98ca65c5c27

可以看到,多出了一個5c,將轉義符(反斜杠)本身轉義,使得后面的%27發揮了作用。

測試如下:

 

 

 

情景三:

使用iconv進行字符集轉換,將UTF-8轉為GBK,同時,set names字符集為GBK。提交%e9%8c%a6即可。

這個情景的大前提是先編碼后轉義:

e98ca6====(iconv)=====>e55c=====(addslashes)====>e55c5c

同樣可以多出一個反斜杠進行利用,在此不再詳述,因為漏洞條件比較苛刻。

 

0x04 安全方案

對於寬字節編碼,有一種最好的修補就是:

(1)使用mysql_set_charset(GBK)指定字符集

(2)使用mysql_real_escape_string進行轉義

原理是,mysql_real_escape_string與addslashes的不同之處在於其會考慮當前設置的字符集,不會出現前面e5和5c拼接為一個寬字節的問題,但是這個“當前字符集”如何確定呢?

就是使用mysql_set_charset進行指定。

上述的兩個條件是“與”運算的關系,少一條都不行。

測試:

 

 

 輸出:

 

 

 效果很明顯

 

參考:https://blog.csdn.net/qq_29419013/article/details/81205291

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM