01 背景知識(shí)
字符集
在了解寬字節(jié)注入之前嵌溢,我們先來(lái)看一看字符集是什么。字符集也叫字符編碼逸雹,是一種將符號(hào)轉(zhuǎn)換為二進(jìn)制數(shù)的映射關(guān)系止潘。
幾種常見(jiàn)的字符集:
-
ASCII
編碼:?jiǎn)巫止?jié)編碼 -
latin1
編碼:?jiǎn)巫止?jié)編碼 -
gbk
編碼:使用一字節(jié)和雙字節(jié)編碼,0x00-0x7F
范圍內(nèi)是一位,和 ASCII 保持一致法绵。雙字節(jié)的第一字節(jié)范圍是0x81-0xFE
-
UTF-8
編碼:使用一至四字節(jié)編碼箕速,0x00–0x7F
范圍內(nèi)是一位,和 ASCII 保持一致朋譬。其它字符用二至四個(gè)字節(jié)變長(zhǎng)表示盐茎。
寬字節(jié)就是兩個(gè)以上的字節(jié),寬字節(jié)注入產(chǎn)生的原因就是各種字符編碼的不當(dāng)操作徙赢,使得攻擊者可以通過(guò)寬字節(jié)編碼繞過(guò)SQL注入防御字柠。
MySQL字符轉(zhuǎn)換
數(shù)據(jù)提交到MySQL數(shù)據(jù)庫(kù),需要進(jìn)行字符集的轉(zhuǎn)換狡赐,使得MySQL數(shù)據(jù)庫(kù)可以對(duì)數(shù)據(jù)進(jìn)行處理窑业,這一過(guò)程一般有以下三個(gè)步驟:
- 收到請(qǐng)求,將請(qǐng)求數(shù)據(jù)從
character_set_client
->character_set_connection
枕屉。 - 內(nèi)部操作常柄,將數(shù)據(jù)從
character_set_connection
->表創(chuàng)建的字符集
。 - 結(jié)果輸出搀擂,將數(shù)據(jù)從
表創(chuàng)建的字符集
->character_set_results
西潘。
當(dāng)執(zhí)行set names "charset"
,相當(dāng)于執(zhí)行
set character_set_client = charset
set character_set_connection = charset
set character_set_results = charset
client 指的是PHP程序
connection 指的是PHP客戶(hù)端與MySQL服務(wù)器之間連接層
results 指的是MySQL服務(wù)器返回給PHP客戶(hù)端的結(jié)果
MySQL常見(jiàn)的亂碼問(wèn)題就是這三個(gè)字符集的設(shè)置不當(dāng)所引起的哨颂。
02 寬字節(jié)注入
這里的演示環(huán)境為 sqli-labs\Less-32
<?php
//including the Mysql connect parameters.
include("../sql-connections/sql-connect.php");
function check_addslashes($string)
{
$string = preg_replace('/'. preg_quote('\\') .'/', "\\\\\\", $string); //escape any backslash
$string = preg_replace('/\'/i', '\\\'', $string); //escape single quote with a backslash
$string = preg_replace('/\"/', "\\\"", $string); //escape double quote with a backslash
return $string;
}
// take the variables
if(isset($_GET['id']))
{
$id=check_addslashes($_GET['id']);
//echo "The filtered request is :" .$id . "<br>";
//logging the connection parameters to a file for analysis.
$fp=fopen('result.txt','a');
fwrite($fp,'ID:'.$id."\n");
fclose($fp);
// connectivity
mysql_query("SET NAMES gbk");
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";
$result=mysql_query($sql);
$row = mysql_fetch_array($result);
if($row)
{
echo '<font color= "#00FF00">';
echo 'Your Login name:'. $row['username'];
echo "<br>";
echo 'Your Password:' .$row['password'];
echo "</font>";
}
else
{
echo '<font color= "#FFFF00">';
print_r(mysql_error());
echo "</font>";
}
}
else { echo "Please input the ID as parameter with numeric value";}
?>
普通注入
當(dāng)用戶(hù)提交
http://127.0.0.1/Less-32/?id=1'
此時(shí)喷市,發(fā)生如下轉(zhuǎn)換
第一步:'
被 check_addslashes
函數(shù)轉(zhuǎn)義為 \'
第二步:在執(zhí)行sql查詢(xún)之前,也即\'
代入MySQL服務(wù)器之前威恼,mysql_query("SET NAMES gbk");
將MySQL的三個(gè)字符集設(shè)置為 gbk 編碼
第三步:character_set_client
告訴MySQL Server
品姓,傳入的是gbk編碼,也就是\'
被當(dāng)做了%5C%27
傳入
第四步:character_set_client
-> character_set_connection
編碼完全一致沃测,數(shù)據(jù)沒(méi)有做任何轉(zhuǎn)換缭黔,所以輸入是%5C%27
,輸出的是%5C%27
第五步:character_set_connection
-> table charset
這里我們需要關(guān)注下所使用的表的字符集
可以看到id
參數(shù)沒(méi)有設(shè)置編碼方式蒂破,不會(huì)對(duì)%5C%27
進(jìn)行處理馏谨。在這里MySQL服務(wù)器將查詢(xún)語(yǔ)句執(zhí)行,并返回結(jié)果附迷。
執(zhí)行的SQL語(yǔ)句為:
$sql="SELECT * FROM users WHERE id='1\'' LIMIT 0,1";
'
被轉(zhuǎn)義無(wú)法進(jìn)行注入
第六步:table charset
-> character_set_results
由于character_set_results
字符集也設(shè)定為 gbk 惧互,保證了輸出內(nèi)容沒(méi)有亂碼。
通過(guò)上面的分析喇伯,我們發(fā)現(xiàn)用戶(hù)提交的數(shù)據(jù)實(shí)際上只被處理了兩次喊儡,一次是check_addslashes
對(duì)危險(xiǎn)字符的處理,另一次是gbk
編碼稻据。所以艾猜,我們可以將以上步驟簡(jiǎn)化為:
數(shù)據(jù) ==== (check_addslashes)====XXX====(GBK)====代入數(shù)據(jù)庫(kù)執(zhí)行的內(nèi)容
寬字節(jié)注入
提交:
http://127.0.0.1/Less-32/?id=1%df'
('
瀏覽器自動(dòng)進(jìn)行url編碼%27
)
根據(jù)以上分析,發(fā)生如下轉(zhuǎn)換:
%df%27
====>(check_addslashes)====>%df%5c%27
====>(GBK)====>運(yùn)'
MySQL執(zhí)行的語(yǔ)句為:
$sql="SELECT * FROM users WHERE id='1運(yùn)'' LIMIT 0,1";
成功將單引號(hào)閉合,可以進(jìn)行SQL注入匆赃。
寬字節(jié)注入2.0
為了避免漏洞淤毛,網(wǎng)站一般會(huì)設(shè)置UTF-8編碼,然后進(jìn)行轉(zhuǎn)義過(guò)濾算柳。但是由于一些不經(jīng)意的字符集轉(zhuǎn)換低淡,又會(huì)導(dǎo)致漏洞。
使用set names UTF-8
指定了UTF-8字符集瞬项,并且也使用轉(zhuǎn)義函數(shù)進(jìn)行轉(zhuǎn)義蔗蹋。有時(shí)候,為了避免亂碼囱淋,會(huì)將一些用戶(hù)提交的GBK字符使用iconv
函數(shù)先轉(zhuǎn)為UTF-8猪杭,然后再拼接入SQL語(yǔ)句。
mysql_query(“set names UTF-8”) ;
$bar =iconv(“GBK”,”UTF-8”, addslashes($_GET[‘’bar])) ;
提交:
http://127.0.0.1/Less-32/?id=1%e5%5c%27
轉(zhuǎn)換:(%e5%5c
轉(zhuǎn)為UTF-8為e9%8c%a6
)
%e5%5c%27
====>(addslashes)====>%e5%5c%5c%5c%27
====(iconv)====>%e9%8c%a6%5c%5c%27
可以看到绎橘,多出了一個(gè)%5c
胁孙,將轉(zhuǎn)義符(反斜杠)本身轉(zhuǎn)義唠倦,使得后面的%27
單引號(hào)發(fā)揮了作用
03 寬字節(jié)注入防御(參考源)
對(duì)于寬字節(jié)編碼称鳞,有一種最好的修補(bǔ)就是:
(1)使用mysql_set_charset(GBK)
指定字符集
(2)使用mysql_real_escape_string
進(jìn)行轉(zhuǎn)義
原理是,mysql_real_escape_string
與addslashes
的不同之處在于其會(huì)考慮當(dāng)前設(shè)置的字符集稠鼻,不會(huì)出現(xiàn)前面e5和5c拼接為一個(gè)寬字節(jié)的問(wèn)題冈止,但是這個(gè)“當(dāng)前字符集”如何確定呢?
就是使用mysql_set_charset
進(jìn)行指定候齿。
上述的兩個(gè)條件是“與”運(yùn)算的關(guān)系熙暴,少一條都不行。
<meta charset="utf-8">
測(cè)試;
輸出: