Metinfo 6.1.2 SQL注入
0x01 前言
這個本來是不想放出來的,因為metinfo這套cms畢竟使用人數還是挺多的,影響範圍也很廣。在前兩個版本我就提交過此漏洞了鑑於一直不修復。所幸放出來給大家共同學習一下。看到這還請提醒廠商儘快修復了。不過我會在最後留下解決方案。

注:只供學習參考,任何用來違法犯罪的與本人無關,違法犯罪快走開,違法犯罪快走開,違法犯罪快走開。
0x02 漏洞分析
漏洞檔案:/var/www/html/metinfo6.1.2/app/system/message/web/message.class.php
漏洞函式:add 37-51行
public function add($info) { global $_M; if(!$_M[form][id]){ $message=DB::get_one("select * from {$_M[table][column]} where module= 7 and lang ='{$_M[form][lang]}'"); $_M[form][id]=$message[id]; } $met_fd_ok=DB::get_one("select * from {$_M[table][config]} where lang ='{$_M[form][lang]}' andname= 'met_fd_ok' and columnid = {$_M[form][id]}"); $_M[config][met_fd_ok]= $met_fd_ok[value]; if(!$_M[config][met_fd_ok])okinfo('javascript:history.back();',"{$_M[word][Feedback5]}"); if($_M[config][met_memberlogin_code]){ if(!load::sys_class('pin', 'new')->check_pin($_M['form']['code'])){ okinfo(-1, $_M['word']['membercode']); } }
漏洞觸發點:
$met_fd_ok=DB::get_one("select * from {$_M[table][config]} where lang ='{$_M[form][lang]}' and name= 'met_fd_ok' and columnid = {$_M[form][id]}");
由於無單引號過濾導致sql注入,這個時候嘗試注入發現關鍵詞全被替換了,無法注入。於是檢視__controler函式發現呼叫了父類的初始化函式。
class feedback extends web
跟進web類,沒有對使用者傳入的資料進行過濾等操作,卻初始化了common類
class web extends common
檢視Common類的初始化函式發現了問題所在
public function __construct() { global $_M;//全域性陣列$_M ob_start();//開啟快取 $this->load_mysql();//資料庫連線 $this->load_form();//表單過濾 $this->load_lang();//載入語言配置 $this->load_config_global();//載入全站配置資料 $this->load_url_site(); $this->load_config_lang();//載入當前語言配置資料 $this->load_url();//載入url資料 }
跟蹤 $this→load_form() 函式
protected function load_form() { global $_M; $_M['form'] =array(); isset($_REQUEST['GLOBALS']) && exit('Access Error'); foreach($_COOKIE as $_key => $_value) { $_key{0} != '_' && $_M['form'][$_key] = daddslashes($_value); } foreach($_POST as $_key => $_value) { $_key{0} != '_' && $_M['form'][$_key] = daddslashes($_value); } foreach($_GET as $_key => $_value) { $_key{0} != '_' && $_M['form'][$_key] = daddslashes($_value); } if(is_numeric($_M['form']['lang'])){//偽靜態相容 $_M['form']['page'] = $_M['form']['lang']; $_M['form']['lang'] = ''; } if($_M['form']['metid'] == 'list'){ $_M['form']['list'] = 1; $_M['form']['metid'] = $_M['form']['page']; $_M['form']['page'] = 1; } if(!preg_match('/^[0-9A-Za-z]+$/', $_M['form']['lang']) && $_M['form']['lang']){ echo "No data in the database,please reinstall."; die(); } }
把COOKIE、POST、GET 傳入 daddslashes函式進行過濾
function daddslashes($string, $force = 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 { if(!defined('IN_ADMIN')){ $string = trim(addslashes(sqlinsert($string))); }else{ $string = trim(addslashes($string)); } } } return $string; }
這裡判斷是否開啟了get_magic_quotes_gpc() 如果沒開啟或者 $force !=0 就進入過濾。
然而最重要的環節在這裡
if(!defined('IN_ADMIN')){ $string = trim(addslashes(sqlinsert($string))); }else{ $string = trim(addslashes($string)); }
判斷 IN_ADMIN 常量是否已經定義了,如果沒定義就使用 $string = trim(addslashes(sqlinsert($string)));
來過濾我們的值
而如果定義了就使用 $string = trim(addslashes($string));
來過濾。
剛剛我們進行sql注入測試失敗而且語句被過濾了, addslashes只過濾特殊字元,所以我們肯定是被 sqlinsert函式給過濾了。導致我們無法sql注入。至於sqlinsert函式我就不貼出來了,我們要做的是饒過這個函式,而不是饒過他這個規則。
defined('IN_ADMIN') 我們需要找這個常量在哪裡定義的。其實寫這句話有點多餘,看過我之前幾篇Metinfo漏洞的話應該就會發現問題了。我寫過一個關於/admin/index.php檔案任意呼叫帶do方法的問題。
然後我們開啟這個檔案看一下。
<?php define('IN_ADMIN', true); $M_MODULE='admin'; if(@$_GET['m'])$M_MODULE=$_GET['m']; if(@!$_GET['n'])$_GET['n']="index"; if(@!$_GET['c'])$_GET['c']="index"; if(@!$_GET['a'])$_GET['a']="doindex"; @define('M_NAME', $_GET['n']); @define('M_MODULE', $M_MODULE); @define('M_CLASS', $_GET['c']); @define('M_ACTION', $_GET['a']); require_once '../app/system/entrance.php'; ?>
沒錯這個常量就是在這個檔案定義的。而且他可以呼叫任意帶do方法。不過問題又來了,漏洞函式是add並且不帶do,那就找呼叫白,於是找到 domessage這個函式。
domessage方法 18-24
public function domessage() { global $_M; if($_M['form']['action'] == 'add'){ $this->check_field(); $this→add($_M['form']);
這裡在呼叫 add方法之前先呼叫了 check_field方法,我們要做的就是保證程式能夠順利執行到add方法,研究了一會發現只需要正常留言,把它傳遞的引數拷下來就ok了。
然後順利執行到了add方法,成功的進行了sql注入。
payload: ofollow,noindex" target="_blank">http://127.0.0.1/metinfo6//admin/index.php?m=web&n=message&c=message&a=domessage&action=add&lang=cn¶137=1¶186=1¶138=1¶139=1¶140=1&id=42
這裡說一下這個id=42,這個值42是不能修改的,目的是讓他返回驗證碼錯誤,饒過了第一層判斷。
if(!$_M[config][met_fd_ok])okinfo('javascript:history.back();',"{$_M[word][Feedback5]}"); if($_M[config][met_memberlogin_code]){ if(!load::sys_class('pin', 'new')->check_pin($_M['form']['code'])){ okinfo(-1, $_M['word']['membercode']); } }
至於為什麼讓他饒過第一層判斷返回驗證碼錯誤,這樣的話我們可以布林盲注,否則只能進行時間注入。這裡不多解釋,自己研究一下。


0x03 解決方法
修改檔案:metinfo6.1.2/app/system/message/web/message.class.php 修改內容: $met_fd_ok=DB::get_one("select * from {$_M[table][config]} where lang ='{$_M[form][lang]}' andname= 'met_fd_ok' and columnid = {$_M[form][id]}"); 修改為: $met_fd_ok=DB::get_one("select * from {$_M[table][config]} where lang ='{$_M[form][lang]}' andname= 'met_fd_ok' and columnid = ‘{$_M[form][id]}’"); 這樣就解決了。
0x04 漏洞驗證指令碼
這裡我做了一個指令碼來看一下效果
想要的在後面下載。
0x05 結束語
指令碼在這。