寬位元組注入原理

2022-06-19 10:00:15 字數 4180 閱讀 1453

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

首先,寬位元組注入與html頁面編碼是無關的,筆者曾經看到

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

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

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

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

由於ascii表示的字元只有128個,因此網路世界的規範是使用unicode編碼,但是用ascii表示的字元使用unicode並不高效。因此出現了中間格式字符集,被稱為通用轉換格式,及utf(universaltransformation format)。

gb2312、gbk、gb18030、big5、shift_jis等這些都是常說的寬位元組,實際上只有兩位元組。寬位元組帶來的安全問題主要是吃ascii字元(一位元組)的現象。

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

在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

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

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設定值進行了一次編碼。

有三種形式

情景一:

在php中使用mysql_query(「set names gbk」);指定三個字符集(客戶端、連線層、結果集)都是gbk編碼。

情景**:

1     .....

2mysql_query

(「set names gbk」);

3$bar = addslashes($_get

[『bar』]) ;

4$sql = 「select password from user where bar=』』」;

5$res = mysql_query($sql

) ;6 ......

提交:

這時,發生如下轉換:

%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     ....

2mysql_query(「set names utf-8」) ;

3$bar =iconv(「gbk」,」utf-8」, addslashes($_get

[『』bar])) ;

4$sql = 「select password from user where bar=』』」 ;

5$res = mysql_query($sql

) ;6 ......

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

提交:變換過程:(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

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

對於寬位元組編碼,有一種最好的修補就是:

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

(2)使用mysql_real_escape_string進行轉義

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

就是使用mysql_set_charset進行指定。

上述的兩個條件是「與」運算的關係,少一條都不行。

測試:

輸出:

效果很明顯

參考:

寬位元組注入繞過原理

首先我們知道,在計算機中每乙個字元為8位,那麼就是1個位元組,所謂的寬位元組注入就是兩個位元組合併從而進行繞過,例如,韓文,日文,繁體字 都屬於寬位元組型別。下面我來個簡單的演示。當我們輸入單引號的時候,那麼會被轉義成 這樣就無法進行注入 但是進行繞過的話,在id引數加入乙個 df 被轉義後成為 d...

寬位元組注入原理與防禦

gbk編碼原理 乙個字元佔1個位元組 兩個位元組以上叫寬位元組 設定 set character set client gbk gbk編碼設定 通常導致編碼轉換的注入問題,尤其是使用php連線mysql資料庫的時候 乙個gbk漢字佔兩個位元組,取值範圍是 編碼位數 第乙個位元組是 129 254 第...

寬位元組注入

大家都知道 df 被php轉義 開啟gpc 用addslashes函式,或者icov等 單引號被加上反斜槓 變成了 df 其中 的十六進製制是 5c 那麼現在 df df 5c 27,如果程式的預設字符集是gbk等寬位元組字符集,則mysql用gbk的編碼時,會認為 df 5c 是乙個寬字元,也就是...