SQL注入防御繞過——寬字節注入


01 背景知識

字符集

在了解寬字節注入之前,我們先來看一看字符集是什么。字符集也叫字符編碼,是一種將符號轉換為二進制數的映射關系。
幾種常見的字符集:

  • ASCII編碼:單字節編碼
  • latin1編碼:單字節編碼
  • gbk編碼:使用一字節和雙字節編碼,0x00-0x7F范圍內是一位,和 ASCII 保持一致。雙字節的第一字節范圍是0x81-0xFE
  • UTF-8編碼:使用一至四字節編碼,0x00–0x7F范圍內是一位,和 ASCII 保持一致。其它字符用二至四個字節變長表示。

寬字節就是兩個以上的字節,寬字節注入產生的原因就是各種字符編碼的不當操作,使得攻擊者可以通過寬字節編碼繞過SQL注入防御。

MySQL字符轉換

數據提交到MySQL數據庫,需要進行字符集的轉換,使得MySQL數據庫可以對數據進行處理,這一過程一般有以下三個步驟:

  1. 收到請求,將請求數據從 character_set_client ->character_set_connection
  2. 內部操作,將數據從character_set_connection-> 表創建的字符集
  3. 結果輸出,將數據從表創建的字符集 -> character_set_results

當執行set names "charset",相當於執行
set character_set_client = charset
set character_set_connection = charset
set character_set_results = charset

client 指的是PHP程序
connection 指的是PHP客戶端與MySQL服務器之間連接層
results 指的是MySQL服務器返回給PHP客戶端的結果

MySQL常見的亂碼問題就是這三個字符集的設置不當所引起的。

02 寬字節注入

這里的演示環境為 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";} ?> 

普通注入

當用戶提交
http://127.0.0.1/Less-32/?id=1'
此時,發生如下轉換
第一步:'check_addslashes 函數轉義為 \'

第二步:在執行sql查詢之前,也即\'代入MySQL服務器之前,mysql_query("SET NAMES gbk");將MySQL的三個字符集設置為 gbk 編碼

第三步:character_set_client告訴MySQL Server,傳入的是gbk編碼,也就是\'被當做了%5C%27傳入

第四步:character_set_client -> character_set_connection編碼完全一致,數據沒有做任何轉換,所以輸入是%5C%27,輸出的是%5C%27

第五步:character_set_connection-> table charset這里我們需要關注下所使用的表的字符集

 
table_charset

 

可以看到id參數沒有設置編碼方式,不會對%5C%27進行處理。在這里MySQL服務器將查詢語句執行,並返回結果。
執行的SQL語句為:
$sql="SELECT * FROM users WHERE id='1\'' LIMIT 0,1";
'被轉義無法進行注入

第六步:table charset -> character_set_results由於character_set_results字符集也設定為 gbk ,保證了輸出內容沒有亂碼。

通過上面的分析,我們發現用戶提交的數據實際上只被處理了兩次,一次是check_addslashes對危險字符的處理,另一次是gbk編碼。所以,我們可以將以上步驟簡化為:

數據 ==== (check_addslashes)====XXX====(GBK)====代入數據庫執行的內容

寬字節注入

提交:
http://127.0.0.1/Less-32/?id=1%df'('瀏覽器自動進行url編碼%27)
根據以上分析,發生如下轉換:
%df%27====>(check_addslashes)====>%df%5c%27====>(GBK)====>運'
MySQL執行的語句為:
$sql="SELECT * FROM users WHERE id='1運'' LIMIT 0,1";成功將單引號閉合,可以進行SQL注入。

寬字節注入2.0

為了避免漏洞,網站一般會設置UTF-8編碼,然后進行轉義過濾。但是由於一些不經意的字符集轉換,又會導致漏洞。
使用set names UTF-8指定了UTF-8字符集,並且也使用轉義函數進行轉義。有時候,為了避免亂碼,會將一些用戶提交的GBK字符使用iconv函數先轉為UTF-8,然后再拼接入SQL語句。

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
轉換:(%e5%5c轉為UTF-8為e9%8c%a6
%e5%5c%27====>(addslashes)====>%e5%5c%5c%5c%27====(iconv)====>%e9%8c%a6%5c%5c%27
可以看到,多出了一個%5c,將轉義符(反斜杠)本身轉義,使得后面的%27單引號發揮了作用

03 寬字節注入防御(參考源)

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

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

(2)使用mysql_real_escape_string進行轉義

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

就是使用mysql_set_charset進行指定。

上述的兩個條件是“與”運算的關系,少一條都不行。
<meta charset="utf-8">

測試;

 
1

輸出:

 
 


免責聲明!

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



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