淺談開源web程序后臺(tái)的安全性
一、前言
不知怎的最近甚是思念校園生活,思念食堂的炒飯。那時(shí)會(huì)去各種安全bbs上刷刷帖子,喜歡看別人寫的一些關(guān)于安全技巧或經(jīng)驗(yàn)的總結(jié);那時(shí)BBS上很多文章標(biāo)題都是:成功滲透XXX,成功拿下XXX。這里便以一篇入侵菲律賓某大學(xué)的文章引出文章的主題,我們先簡(jiǎn)要看一下過程。大學(xué)網(wǎng)站使用了名為joomla的開源web程序,(1)青年使用一個(gè)joomla已經(jīng)公開的漏洞進(jìn)入web后臺(tái)(2)青年使用joomla后臺(tái)上傳限制不嚴(yán)的缺陷上傳了一個(gè)webshell(3)控制主機(jī)贈(zèng)送我國(guó)國(guó)旗。
原來入侵一臺(tái)主機(jī)如此容易,管理員果斷給web程序打上安全補(bǔ)丁。管理員的工作是結(jié)束了,作為安全從業(yè)人員再一想是不是joomla后臺(tái)這里可以上傳webshell是不是有問題呢,如果joomla后臺(tái)不能上傳webshell,是不是可以減少入侵的可能和損失。下面進(jìn)入本文的主題:web后臺(tái)程序的安全性。
二、簡(jiǎn)介
國(guó)內(nèi)很多站點(diǎn)都是基于開源論壇、cms搭建的,比如discuz、phpwind、dedecms等。這些程序都是國(guó)內(nèi)開源web程序中的佼佼者,也比較注重安全性。平時(shí)大家關(guān)注比較多的是sql注入、xss這些可以直接竊取用戶數(shù)據(jù)的漏洞。網(wǎng)上因?yàn)槿蹩诹畋蝗肭值陌咐龜?shù)不勝數(shù),此外用戶數(shù)據(jù)泄漏事件時(shí)而發(fā)生,單純靠密碼防護(hù)的后臺(tái)被突破,被社工的可能性越來越大。獲取一個(gè)管理后臺(tái)密碼后,再結(jié)合后臺(tái)程序的任意代碼執(zhí)行、文件包含或命令注入等漏洞得到一個(gè)shell,竊取用戶資料不是什么難事。此時(shí)后臺(tái)程序的安全性成為一個(gè)短板。
Discuz是一款流行的論壇程序,筆者這里就以它的后臺(tái)程序?yàn)槔?jiǎn)單分析一下其安全性,下面直接看一些漏洞案例(Discuz最新版本已打補(bǔ)丁,請(qǐng)用戶及時(shí)升級(jí)到最新版-Discuz! X3.1 R20140101)。
三、案例分析
Tips:下文提到的$settingnew是discuz后臺(tái)存儲(chǔ)表單數(shù)據(jù)的變量,后臺(tái)用戶可控。
案例一:用戶輸入數(shù)據(jù)過濾邏輯不當(dāng)
漏洞文件:X3\source\admincp\admincp_setting.php
分析:
// 1、alice修改$settingnew['extcredits']非數(shù)組
if(is_array($settingnew['extcredits'])) {
foreach($settingnew['extcredits'] as $key => $value) {
// 2、給$settingnew['initcredits'][1]傳入phpinfo();,非數(shù)組繞過intval轉(zhuǎn)換
$settingnew['initcredits'][$i] = intval($settingnew['initcredits'][$i]);
... 省略 ...
for($i = 1; $i <= 8; $i++) {
// 3、 phpinfo();被賦值給$initformula
$initformula = str_replace('extcredits'.$i, $settingnew['initcredits'][$i], $initformula);
}
// 4、phpinfo()帶入eval執(zhí)行
eval("\$_G['setting']['initcredits'] = round($initformula);");
案例二:二次注入
簡(jiǎn)單介紹一下二次注入,惡意用戶alice在A處傳入惡意數(shù)據(jù)并被存儲(chǔ)到數(shù)據(jù)庫(kù),在A處不直接導(dǎo)致安全問題;B處引用到A處存儲(chǔ)的數(shù)據(jù),從而觸發(fā)安全問題。
漏洞文件:X3\source\admincp\admincp_setting.php
分析:
// 1、alice上傳一個(gè)圖片木馬假設(shè)為1.gif; alice設(shè)置$settingnew['seccodedata']['type']值為1.gif\0:xx(根據(jù)圖片地址做適當(dāng)目錄跳轉(zhuǎn));該值未作任何過濾存入數(shù)據(jù)庫(kù)
if($settingnew['seccodedata']['type'] == 0 || $settingnew['seccodedata']['type'] == 2) {
$seccoderoot = 'static/image/seccode/font/en/';
} elseif($settingnew['seccodedata']['type'] == 1) {
$seccoderoot = 'static/image/seccode/font/ch/';
}漏洞文件:source\module\misc\misc_seccode.php
// 2、$_G['setting']['seccodedata']['type']值來自于數(shù)據(jù)庫(kù),即為1處傳入的1.gif\0:xx
if(!is_numeric($_G['setting']['seccodedata']['type'])) {
$etype = explode(':', $_G['setting']['seccodedata']['type']);
if(count($etype) > 1) {
// 3、 \0截?cái)嗟玫?codefile為圖片小馬(也可使用././././多個(gè)路徑符方法截?cái)?
$codefile = DISCUZ_ROOT.'./source/plugin/'.$etype[0].'/seccode/seccode_'.$etype[1].'.php';
... 省略 ...
if(file_exists($codefile)) {
// 4、圖片木馬被include得到webshell
@include_once $codefile;
案例三:程序升級(jí)新增邏輯導(dǎo)致的漏洞
漏洞文件:X3\source\admincp\admincp_adv.php
// 1、alice上傳一個(gè)圖片木馬假設(shè)為1.gif; alice傳入type參數(shù)值為1.gif\0:xx(根據(jù)圖片地址做適當(dāng)目錄跳轉(zhuǎn))
$type = $_GET['type'];
... ...
if($type) {
//2、得到$etype為1.gif\0
$etype = explode(':', $type);
if(count($etype) > 1) {
//3、$advfile值被\0截?cái)啵瑸閳D片木馬路徑1.gif
$advfile = DISCUZ_ROOT.'./source/plugin/'.$etype[0].'/adv/adv_'.$etype[1].'.php';
$advclass = 'adv_'.$etype[1];
}
... 省略 ...
//4、包含圖片木馬,得到webshell
if(file_exists($advfile)) {
require_once $advfile;
對(duì)比下X2.5版本的邏輯,此處漏洞完全是因?yàn)樾略龃a導(dǎo)致的。
$type = $_GET['type'];
$target = $_GET['target'];
$typeadd = '';
if($type) {
$advfile = libfile('adv/'.$type, 'class');
if(file_exists($advfile)) {
require_once $advfile;
案例四:漏洞修補(bǔ)不完善
漏洞文件:X3\api\uc.php
分析:
//1、config_ucenter.php內(nèi)容部分截取如下:define('UC_API', 'http://localhost/bbs/uc_server');
$configfile = trim(file_get_contents(DISCUZ_ROOT.'./config/config_ucenter.php'));
... ...
//2、$UC_AP外部可控,alice傳入$UC_API的值為xyz');eval($_POST[cmd]; 得到$configfile值為define('UC_API', 'xyz\');eval($_POST[cmd];'); xyz后面的引號(hào)被轉(zhuǎn)義。
$configfile=preg_replace("/define\('UC_API',\s*'.*?'\);/i", "define('UC_API','".addslashes($UC_API)."');", $configfile);
//3、將define('UC_API', 'xyz\');eval($_POST[cmd];');寫入配置文件
if($fp = @fopen(DISCUZ_ROOT.'./config/config_ucenter.php', 'w')) {
@fwrite($fp, trim($configfile));
@fclose($fp);
}
//4、alice再次傳入$UC_API的值為xyz,preg_replace使用的正則表達(dá)式是 define\('UC_API',\s*'.*?'\); .*?'非貪婪匹配,匹配到第一個(gè)引號(hào)結(jié)束, 之前的轉(zhuǎn)義符被替換xyz\替換為xyz,從而得到$configfile值為 define('UC_API', 'xyz');eval($_POST[cmd];');寫入配置文件得到webshell。
這個(gè)問題早在2010年外部已經(jīng)公開,官方已及時(shí)發(fā)出補(bǔ)丁
詳情請(qǐng)參考:http://www.oldjun.com/blog/index.php/archives/76/
四、總結(jié)
上面這些例子主要是筆者實(shí)踐經(jīng)驗(yàn)的一些總結(jié),不一定全面,希望能給大家拓展一些思路;比如上述提到的二次注入,$settingnew['seccodedata']['type']這個(gè)變量沒過濾,$settingnew的其他數(shù)組也可能沒過濾,也確實(shí)存在多處類似的問題,大家可以自行去嘗試一下。關(guān)于代碼審計(jì)的方法主要有兩個(gè)大方向:(1)危險(xiǎn)函數(shù)向上追蹤輸入;(2)追蹤用戶輸入是否進(jìn)入危險(xiǎn)函數(shù);這里的危險(xiǎn)函數(shù)關(guān)于危險(xiǎn)函數(shù)主要包括代碼執(zhí)行相關(guān):eval、assert,文件包含:include、require等,命令執(zhí)行:system、exec等,寫文件:fwrite、file_put_contents等;
代碼審計(jì)的方法這里推薦兩篇文章:
https://code.google.com/p/pasc2at/wiki/SimplifiedChinese
http://wenku.baidu.com/view/c85be95a3b3567ec102d8a12.html
五、反思
1、一切輸入都是有害的;
后臺(tái)程序的用戶輸入相比前臺(tái)主要增加了后臺(tái)表單的數(shù)據(jù),此外有些后臺(tái)支持上傳文件(如dz1.5的自定義sql),上傳文件的內(nèi)容也屬于輸入;這些輸入都屬于用戶范圍。一定要做嚴(yán)格的控制和過濾。
2、安全意識(shí);
其實(shí)很多漏洞的產(chǎn)生并不是技術(shù)問題導(dǎo)致的,而是我們?nèi)狈Π踩庾R(shí),不重視安全而釀成的慘劇。尤其是第三個(gè)和第四個(gè),完全不應(yīng)該發(fā)生;需要對(duì)開發(fā)人員做安全宣導(dǎo)和基本的安全培訓(xùn)。
3、漏洞Review;
(1)開發(fā)人員收到漏洞后要對(duì)漏洞產(chǎn)生的原因做總結(jié),并Review代碼中是否有類似的問題。有些時(shí)候開發(fā)人員僅僅是修補(bǔ)了安全人員或白帽子提供的漏洞點(diǎn),另外一處代碼有類似的問題沒修補(bǔ)繼續(xù)爆出漏洞,無窮無盡。這樣做還會(huì)帶來更大的隱患,黑客是非常樂意并擅長(zhǎng)總結(jié)反思的,每一個(gè)補(bǔ)丁其實(shí)也是給黑客拓展了思路,如果修補(bǔ)不完全后果很嚴(yán)重。
(2)開發(fā)人員修補(bǔ)完成后安全人員需要進(jìn)行測(cè)試確認(rèn),上述的案例四就是鮮明的例子。有條件的情況下安全人員應(yīng)該整理一些常見漏洞修復(fù)指引,這樣也可以提高工作效率。