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