多字節(jié)編碼由來(lái)
我們先來(lái)看看最常用的,最小字符集是ascii,對(duì)應(yīng)的二級(jí)制可以表示為:00-7F 編碼 。它也是我們計(jì)算機(jī)使用最早通用的字符集。前期幾乎可以表示所有英文字符。后來(lái),更多使用計(jì)算機(jī)國(guó)家加入后,我們就想在計(jì)算機(jī)中表示中文字符。我們知道常見(jiàn)中文就有7000多個(gè)字符。ascii碼就只有128字符,只有0-127編碼位置,遠(yuǎn)遠(yuǎn)不夠用了。因此,我們就開(kāi)始制作更大字符集,并且保證兼容ascii編碼。要支持更多字符,選擇更大字符集。我們只能用多個(gè)字節(jié)來(lái)描述一個(gè)字符了。為了很好的與ascii碼,區(qū)分開(kāi)來(lái)!一般做法是:每個(gè)字節(jié)值都大于>7F,如果是2個(gè)字節(jié),那么就是:[>7F][>7F]。這樣編碼,保證很好的與ascii區(qū)分開(kāi),并且擴(kuò)大了字符集。像gb2312范圍在[0xA1-0xF7][0xA1-0xFE](中間很多沒(méi)有填滿),它完全保證所有字節(jié)在A0之上,也就完全滿足在7F之上了。
GBK編碼漏洞緣由
通過(guò)上面的分析,我們知道gb2312編碼是很好的跟ascii碼分開(kāi)了。 那么我們看看,GBK編碼呢,它是完全兼容gb2312(就是說(shuō)在gb2312字符集中每個(gè)字符位置,與gbk字符集里面位置完全一致,而且包含于gb2312),但是,它有2萬(wàn)多個(gè)字符。從上面看,只能選擇往下排序了。 就是從A1A0往下排了,我們發(fā)現(xiàn)它編碼實(shí)際范圍是:[0x81-0xFE]([0x40-0x7E|0x80-0xFE] ) (GBK編碼),我們發(fā)現(xiàn)由2個(gè)字節(jié)組成,首字節(jié)范圍在7F之上,而第2個(gè)字節(jié),有一部分在0x40-0x7E了。這就是導(dǎo)致bug原因。我們看看下面例子吧!
從ASCII碼表中,我們知道0x40-0x7E 包含字符有:“
![](/d/20211017/46e46785c103b1d26b4ec0a69ff7f8f3.gif)
選擇gbk編碼,運(yùn)行上面代碼,就一條簡(jiǎn)單的命令導(dǎo)致出現(xiàn)錯(cuò)誤,說(shuō)字符串 賦值 沒(méi)有結(jié)束! 呵呵,估計(jì)很多人看到這個(gè)就會(huì)認(rèn)為是php 出Bug了。但是,如果我們變成$a=”誠(chéng)a”,發(fā)現(xiàn)可以正常運(yùn)行了。是不是覺(jué)得很奇葩啦!!
原因分析:我們知道文件存在磁盤都是二級(jí)制方式,無(wú)論你存什么字符,最終都是以該字符的在所選字符集中字符編碼保存。php解析時(shí)候,最小分析單元是字節(jié)。無(wú)論你是多字節(jié)還是單字節(jié)字符。最終都是按照字節(jié)來(lái)處理的?!罢\(chéng)” GBK編碼是 D55C,php按字節(jié)來(lái)解釋,5C對(duì)應(yīng)字符是“\” 字符。后面直接跟個(gè)‘”',相當(dāng)于被轉(zhuǎn)義了。 因?yàn)闆](méi)有閉合,因此出現(xiàn)錯(cuò)誤!。大家看出問(wèn)題所在了吧,按自己處理的話,會(huì)自然把多字節(jié)拆成單字節(jié)了。這樣就會(huì)出現(xiàn)很多奇怪問(wèn)題了。
總結(jié):通過(guò)上面講解,我們知道了多字節(jié)編碼過(guò)程,以及GBK導(dǎo)致簡(jiǎn)單程序出錯(cuò)的原因。其實(shí),我們很多程序語(yǔ)言里面,都會(huì)以單個(gè)字節(jié)來(lái)解析的。這樣,當(dāng)你選擇多字節(jié)GBK編碼中文時(shí),剛好有字節(jié)落在特殊位置,將會(huì)出現(xiàn)奇怪錯(cuò)誤問(wèn)題。而且,還將給系統(tǒng)帶來(lái)本身的漏洞,后面我再說(shuō)說(shuō),GBK編碼缺陷,導(dǎo)致漏洞、以及專門利用該編碼漏洞缺陷進(jìn)行系統(tǒng)入侵!好了,先到這里了,歡迎交流!
GBK字符編碼(字符集)缺陷攻擊(注入)原理
上一節(jié),我們分析了。選擇不同編碼可能會(huì)導(dǎo)致程序帶來(lái)本身潛在的漏洞。這次我們以GBK編碼為例,看看怎么樣通過(guò)該編碼注入到系統(tǒng)中。目前很多開(kāi)源系統(tǒng)都存在類似的注入問(wèn)題。我們先來(lái),從一個(gè)Demo開(kāi)始!
GBK字符集漏洞注入原理
?php
$u=isset($_GET['u'])? $_GET['u']:'';
$u=addslashes($u);
$sql = "select * from user where user='$u'";
以上是我們寫(xiě)的一個(gè)測(cè)試?yán)樱℅BK編碼),現(xiàn)在很多開(kāi)源系統(tǒng),比較少的進(jìn)行統(tǒng)一參數(shù)過(guò)濾,有時(shí)候?yàn)榱朔乐棺⑷耄椭苯訉?duì)參數(shù)進(jìn)行轉(zhuǎn)義處理。我們看看,這樣一個(gè)例子,我們?cè)趺礃幼⑷脒M(jìn)系統(tǒng)!
步驟 |
備注 |
1.傳入值%D5%27 or 1=1# |
u參數(shù)參入上面值 (%27 對(duì)應(yīng)是“'” 單引號(hào)字符) |
2.GET獲取的值 |
0xD50x27 or 1=1# |
3.Addslashes后值 |
0xD50x5C0x27 or 1=1 (意思是:誠(chéng)' or 1=1#') #字符后面被注釋掉 |
4.sql值 將變成 |
select * from user where user='誠(chéng)' or 1=1#' |
#號(hào)是sql注釋符號(hào),后面字符將截取掉 |
![](/d/20211017/5f02a8d761b4c5ddad8b834229680d45.gif)
GPC轉(zhuǎn)義打開(kāi),或者是通過(guò)addslashes函數(shù),會(huì)自動(dòng)在字符是單引號(hào)(‘)、雙引號(hào)(")、反斜線(\)與 NUL(NULL 字符)等字符前面增加“\”字符(0x5c),例子里面,我們采用一個(gè)特殊前面字節(jié)0xD5,它將與該字節(jié)組合變成:0xD50x5c ,剛好是gbk字符集中字符:”誠(chéng)“ 了。 后面的0×27這個(gè)單引號(hào)被保留下來(lái)了!
GBK字符集漏洞注入總結(jié)
呵呵,這個(gè)很有意思吧,好了。我們來(lái)總結(jié)下,這類注入是2個(gè)條件的。第一是:gbk編碼,第二是:程序采用了轉(zhuǎn)義方法,轉(zhuǎn)義了輸入。 這2個(gè)條件不苛刻,目前大部分開(kāi)源系統(tǒng)都有g(shù)bk,utf-8編碼的源碼,剩下的就去看看,源碼里面有沒(méi)有用類似轉(zhuǎn)義方法,過(guò)濾字符串了。如果有,那么這個(gè)系統(tǒng)某個(gè)功能,你可以去滲透下了。這個(gè)編碼漏洞,網(wǎng)上面提的很多,不過(guò)很多時(shí)候,沒(méi)有引起開(kāi)發(fā)人員的足夠重視,還是在不斷的重現(xiàn)!
那么我們?nèi)绻⑷胍粋€(gè)參數(shù),我們?cè)撨x擇什么樣的入?yún)?shù)呢?其實(shí)這種轉(zhuǎn)義字符是單引號(hào)(‘)、雙引號(hào)(")、反斜線(\)與 NUL(NULL 字符),我們這些字符往往在程序中有特殊作用,我們只需要在前面加一個(gè)在>7F字符,后面接一個(gè)%27(‘)、%22(")、%5C(\)、%00(NULL 字符),就可以自己讓這4個(gè)字符,可以逃脫轉(zhuǎn)義了。
好了,這個(gè)漏洞原理及注入過(guò)程分析就這些了。我們開(kāi)發(fā)時(shí)候,需要注意這個(gè)問(wèn)題,特別是使用GBK編碼開(kāi)發(fā)程序,要有這個(gè)方面的預(yù)備知識(shí),對(duì)于自己開(kāi)發(fā)安全的代碼會(huì)有幫助的。更多的GBK編碼,可以看http://doc.chacuo.net/gbk !(這里有很多落在5c中文字符呢)也歡迎討論!