1. 程式人生 > >5. 寬字符註入詳解與實戰

5. 寬字符註入詳解與實戰

構造 詳解 這就是 取數 報錯 query 服務器 html 連接mysql

寬字節註入源於程序員設置MySQL連接時的錯誤配置,如下:

set character_set_client=gbk

這樣的配置會引發編碼轉換從而導致繞過某些防護實現註入漏洞。具體分析一下原理:

  1. 正常情況下GPC開啟或者使用addslashes函數過濾GET或POST提交的參數時,我們測試輸入的‘,就會被轉義為\‘;

  2. 若存在寬字節註入,輸入%df%27時,經過單引號的轉義變成了%df%5c%27,之後再數據庫查詢語句進行GBK多字節編碼,即一個中文占用兩個字節,一個英文同樣占用兩個字節且在漢字編碼範圍內兩個編碼為一個漢字。然後MySQL服務器會對查詢語句進行GBK編碼即%df%5c轉換成漢字"運",單引號逃逸出來,從而繞過轉義造成註入漏洞。

現在基本都會將mysql的連接配置設置為:

[set character_set_client=binary]

來解決這個問題,這篇博客將介紹php中因為編碼或字符編碼轉換導致的註入問題。

mysql中的寬字符註入

測試搭建學習的環境利用了phithon內容管理系統,看代碼

技術分享圖片

SQL語句是SELECT * FROM news WHERE tid=‘{$id}‘,根據文章的id把文章從news表中提取出來,在$sql之前,我們只用了限制函數addslashes函數,對$id進行轉義,只要我們輸入參數在單引號中,就逃逸不出單引號的限制,從而無法註入。

技術分享圖片

我們這裏利用的是mysql的一個特性,mysql在使用GBK編碼的時候,會認為兩個字節是一個漢字(前一個ascii碼要大於128,才到漢字範圍),我們測試輸入%df‘

已經報錯,看到報錯,說明sql語句出錯,看到出錯說明可以註入。報錯的原因就是多了一個單引號,而單引號前面的反斜杠不見啦。這就是mysql的特性,因為gbk是多字節編碼,它認為兩個字節代表一個字符,所以%df和後面的%5c變成了漢字“運”,而’逃逸了出來。

因為是兩個字節代表一個漢字,我們嘗試%df%df%27

技術分享圖片

不報錯了,因為%df%df組成了漢字"哌",%5c%27不是漢字,仍然是\‘

mysql如何判斷一個字符是不是一個漢字,根據gbk編碼,第一個字節的ascii碼大於128,基本上就行,若不用%df而用%a1也可以

技術分享圖片

%a1%5c雖然不是一個漢字,但一定會被mysql認為是一個寬字符,所以就能讓後面的%27逃逸出來,構造一個exp,查詢管理人員的賬號密碼。

技術分享圖片

GB12和GBK的區別

gb2312和gbk都是寬字節家族醫院,但是當把數據庫編碼設置為關閉gb2312時,結果就不能註入

技術分享圖片

這主要是gb2312編碼取值範圍的事情,它高位範圍0xA1~0xF7,低位範圍是0xA1~0xFE,\是%5c,是不在低範圍中的,即其根本不是gb2312遍嗎,故其不會被吃掉。故只要低位的範圍中含有0x5c的編碼,就可以進行寬字節的註入

利用mysql_real_escape_string解決問題

一些cms把addslashes替換為mysql_real_escape_string來防止寬字節的註入

技術分享圖片

我們若解決需要做的指定php連接mysql的字符集。我們需要在執行sql語句之前調用一下mysql_set_charset函數,設置當前的字符集為gbk,來避免問題

技術分享圖片

寬字節註入修復

character_set_client=‘binary‘設置為binary(二進制),只需要在所有的sql語句前指定一下連接的形式為二進制:mysql_query("SET character_set_connection=gbk, character_set_results=gbk,character_set_client=binary", $conn);,當我們的mysql接受到客戶端的數據後,會認為他的編碼是character_set_client,然後會將換成character_set_connection的編碼,然後在進入具體表和字段後,再轉換成字段對應的編碼,然後當查詢結果產生後,會從表和字段編碼,轉換成character_set_results編碼,返回給客戶端。

技術分享圖片

這個方法避免寬字節的註入還是有效的,但是如果開發者畫蛇添足的增加一些東西,會讓前期的努力前功盡棄。

iconv造成的嚴重後果

很多cms會將接收到的數據,調用這樣一個函數,轉換其編碼:iconv(‘utf-8‘,‘gbk‘,$_GET[‘id‘]);,目的一般是避免亂碼,特別是搜索框的位置

技術分享圖片

可以發現,在sql語句執行前,將character_set_client設置成了binary,所以避免寬字節的註入問題。但之後其調用了iconv將已經過濾的參數$id給轉換了一下,測試一下

技術分享圖片

報錯說明我們錦被iconv從utf-8轉換成gbk後,變成了%e5%5c,而後面的‘被addslashes變成了%5c%27,這樣組合起來就是%e5%5c%5c%27,兩個%5c就是\,正好把反斜杠轉義了,導致‘逃逸出單引號,產生註入。利用的是將\轉移掉。

利用iconv將gbk轉換成utf-8,則可以直接用寬字節註入的姿勢來。gbk漢字2字節,utf-8漢字是3字節,若把gbk轉換成utf-8,則php會每兩個字節一轉換。所以,如果\’前面的字符是奇數的話,勢必會吞掉\,’逃出限制。

總結

  1. gbk編碼造成的寬字符註入問題,解決方法是設置character_set_client=binary。

  2. 矯正人們對於mysql_real_escape_string的誤解,單獨調用set name=gbk和mysql_real_escape_string是無法避免寬字符註入問題的。還得調用mysql_set_charset來設置一下字符集。

  3. 謹慎使用iconv來轉換字符串編碼,很容易出現問題。只要我們把前端html/js/css所有編碼設置成gbk,mysql/php編碼設置成gbk,就不會出現亂碼問題。不用畫蛇添足地去調用iconv轉換編碼,造成不必要的麻煩。

代碼審計實戰

對騎士cms審計時發現在plus/ajax_street.php

技術分享圖片

在之前配置文件設置的是mysql_query("SET character_set_connection=" . $dbcharset . ", character_set_results=" . $dbcharset . ", character_set_client=binary", $this->linkid);,其中利用了iconv函數造成致命的錯誤,同時分析發現頁面將查詢結果回顯回來,構造一些union的查詢語句即可獲取數據庫的敏感信息

漏洞的利用

測試有幾個字段,發現category表一共有9個字段,所以可以構造獲取數據庫用戶和先關信息的exp

技術分享圖片

然後利用union的查詢語句爆出可利用的列為4,8,exp:

http://localhost/74cms/upload/plus/ajax_street.php?act=key&key=-%e9%8c%a6‘ union select 1,2,3,4,5,6,7,8,9-- -),

然後是爆出數據庫和用戶名等相關信息

技術分享圖片

補充

GBK編碼中的兩個字符是一個漢字,第一個字符需要大於128

----------------文章轉載自微信公眾號---信安之路

5. 寬字符註入詳解與實戰