作者:phith0n@長亭科技
Metinfo 8月1日升級了版本,修復了一個影響小于等于 5.3.17 版本(幾乎可以追溯到所有5.x版本)的 SQL 注入漏洞。這個 SQL 注入漏洞不受軟 WAF 影響,可以直接獲取數據,影響較廣。
0x01. 漏洞原理分析
漏洞出現在 /include/global.func.php 文件的 jump_pseudo 函數:
<?php
/*靜態頁面跳轉*/
function jump_pseudo(){
global $db,$met_skin_user,$pseudo_jump;
global $met_column,$met_news,$met_product,$met_download,$met_img,$met_job;
global $class1,$class2,$class3,$id,$lang,$page,$selectedjob;
global $met_index_type,$index,$met_pseudo;
if($met_pseudo){
$metadmin[pagename]=1;
$pseudo_url=$_SERVER[HTTP_X_REWRITE_URL]?$_SERVER[HTTP_X_REWRITE_URL]:$_SERVER[REQUEST_URI];
$pseudo_jump=@strstr($_SERVER['SERVER_SOFTWARE'],'IIS')&&$_SERVER[HTTP_X_REWRITE_URL]==''?1:$pseudo_jump;
$dirs=explode('/',$pseudo_url);
$dir_dirname=$dirs[count($dirs)-2];
$dir_filename=$dirs[count($dirs)-1];
if($pseudo_jump!=1){
$dir_filenames=explode('?',$dir_filename);
switch($dir_filenames[0]){
case 'index.php':
if(!$class1&&!$class2&&!$class3){
if($index=='index'){
if($lang==$met_index_type){
$jump['url']='./';
}else{
$jump['url']='index-'.$lang.'.html';
}
}else{
if($lang==$met_index_type){
$jump['url']='./';
}else{
$id=$class3?$class3:($class2?$class2:$class1);
if($id){
$query="select * from $met_column where id='$id'";
}else{
$query="select * from $met_column where foldername='$dir_dirname' and lang='$lang' and (classtype='1' or releclass!='0') order by id asc";
}
$jump=$db->get_one($query);
$psid= ($jump['filename']<>"" and $metadmin['pagename'])?$jump['filename']:$jump['id'];
if($jump[module]==1){
$jump['url']='./'.$psid.'-'.$lang.'.html';
}else if($jump[module]==8){
$jump['url']='./'.'index-'.$lang.'.html';
}
else{
if($page&&$page!=1)$psid.='-'.$page;
$jump['url']='./'.'list-'.$psid.'-'.$lang.'.html';
}
}
}
...
}
代碼截的不全,只關注一下這幾個操作:
$pseudo_url=$_SERVER[HTTP_X_REWRITE_URL]?$_SERVER[HTTP_X_REWRITE_URL]:$_SERVER[REQUEST_URI];: 從$_SERVER[HTTP_X_REWRITE_URL]中獲取$pseudo_url變量$dirs=explode('/',$pseudo_url);:將$pseudo_url變量用斜線分割成$dirs數組$dir_dirname=$dirs[count($dirs)-2];:獲取$dirs的倒數第二個元素作為$dir_dirname變量$query="select * from $met_column where foldername='$dir_dirname' and lang='$lang' and (classtype='1' or releclass!='0') order by id asc";:$dir_dirname變量被拼接進SQL語句
所以,通過分析可知,$_SERVER[HTTP_X_REWRITE_URL]的一部分,最終被拼接進 SQL 語句。那么,如果
Metinfo 沒有對 HTTP 頭進行驗證的情況下,將導致一個 SQL 注入漏洞。
看一下 Metinfo 對于變量的獲取方式:
<?php
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);
}
}
使用daddslashes函數過濾GPC變量,daddslashes這個函數確實很討厭,不光有轉義,而且有很不友好的軟 WAF。但我們這里這個注入點是來自于 SERVER 變量,所以是不受軟 WAF 影響的。
0x02. 漏洞利用缺陷
那么,我們看看如何才能進入這個注入的位置。
jump_pseudo函數前面有一些條件語句,歸納一下主要有下面幾個:
- 需要滿足
if($met_pseudo)... - 需要滿足
if($pseudo_jump!=1)... - 需要滿足
switch($dir_filenames[0]){ case 'index.php':... - 需要滿足
if(!$class1&&!$class2&&!$class3)... - 不能滿足
if($index=='index')... - 不能滿足
if($lang==$met_index_type)...
翻譯成漢字,大意就是:
$met_pseudo必須為真。$met_pseudo這個變量是指系統是否開啟了偽靜態,也就說這個漏洞需要開啟偽靜態才能夠利用。$pseudo_jump不等于1。這個條件,只要$_SERVER[HTTP_X_REWRITE_URL]有值即可滿足。$dir_filenames[0]必須等于'index.php',這個變量是可控的。class1、class2、class3不能有值。這個條件,只要我訪問的是index.php,并且不主動傳入這三個參數,即可滿足。$index不能等于'index',這個變量也是可控的,傳入參數index=xxxx即可$lang不能等于$met_index_type這6個條件語句中,2~5中的變量都可控,1中的變量只要開啟偽靜態即可滿足,唯獨6需要單獨分析一下。
$lang是我們傳入的參數,代表給訪客顯示的語言是什么。Metinfo 默認安裝時,將存在3種語言:簡體中文(cn)、英文(en)、繁體中文(tc),而$met_index_type表示默認語言類型,默認是中文,也就是cn。
而 Metinfo 的配置(包括偽靜態相關的配置),是和語言有關系的,不同語言的配置不相同。默認情況下,如果管理員在后臺開啟偽靜態,將只會修改lang=cn時的配置。
那么,正常情況下,我們傳入index.php?lang=cn,將會導致if($lang==$met_index_type)...這個條件成立,也就沒法進入SQL注入的語句中;如果我們傳入index.php?lang=en,又導致偽靜態配置恢復默認,也就是$met_pseudo = 0,導致進不去步驟1的if語句;如果我們傳入一個不存在的lang,比如index.php?lang=xxx,將會導致報錯:No data in the database,please reinstall.
這就比較蛋疼。此時,就需要利用到Mysql的一個特性。
0x03. Mysql 大小寫特性回顧
Mysql 對于內容的存儲方式,有如下兩個概念:字符集(character set)和collation(比對方法)。
二者組合成 Mysql 的字符格式,一般來說分為這兩類:
<character set>_<language/other>_<ci/cs>
<character set>_bin
比如,最常用的utf8_general_ci,就是第一種格式。
我們這里需要關注的就是最后一串:ci、cs、bin,這三個究竟是什么?
ci 其實就是 case insensitive (大小寫不敏感)的縮寫, cs 是 case sensitive (大小寫敏感)的縮寫。也就是說,當我們用的字符格式是utf8_general_ci時,Mysql中比對字符串的時候是大小寫不敏感的。
bin 指的是比較的時候,按照二進制的方式比較,這種情況下就不存在大小寫的問題了。bin方式還可以解決有些小語種上的特性,這個就不展開說了。
我們隨便找了個數據表,做個小實驗:
可見上圖,雖然我查詢的 SQL 語句是SELECT * FROM `wp_users` WHERE `user_login`='AdmIN',但實際上查詢出來了用戶名是admin的用戶賬戶。
0x04. 完成漏洞利用
回到 Metinfo,我們可以利用 0x03 中說到的 Mysql 特點,來繞過if($lang==$met_index_type)...的判斷。
我們來看看 Metinfo 是如何獲取系統配置的:
<?php
/*默認語言*/
$met_index_type = $db->get_one("SELECT * FROM $met_config WHERE name='met_index_type' and lang='metinfo'");
$met_index_type = $met_index_type['value'];
$lang=($lang=="")?$met_index_type:$lang;
$langoks = $db->get_one("SELECT * FROM $met_lang WHERE lang='$lang'");
if(!$langoks)die('No data in the database,please reinstall.');
if(!$langoks[useok]&&!$metinfoadminok)okinfo('../404.html');
if(count($met_langok)==1)$lang=$met_index_type;
/*讀配置數據*/
$_M[config][tablepre]=$tablepre;
$query = "SELECT * FROM $met_config WHERE lang='$lang' or lang='metinfo'";
$result = $db->query($query);
while($list_config= $db->fetch_array($result)){
$_M[config][$list_config['name']]=$list_config['value'];
if($metinfoadminok)$list_config['value']=str_replace('"', '"', str_replace("'", ''',$list_config['value']));
$settings_arr[]=$list_config;
if($list_config['columnid']){
$settings[$list_config['name'].'_'.$list_config['columnid']]=$list_config['value'];
}else{
$settings[$list_config['name']]=$list_config['value'];
}
if($list_config['flashid']){
$list_config['value']=explode('|',$list_config['value']);
$falshval['type']=$list_config['value'][0];
$falshval['x']=$list_config['value'][1];
$falshval['y']=$list_config['value'][2];
$falshval['imgtype']=$list_config['value'][3];
$list_config['mobile_value']=explode('|',$list_config['mobile_value']);
$falshval['wap_type']=$list_config['mobile_value'][0];
$falshval['wap_y']=$list_config['mobile_value'][1];
$met_flasharray[$list_config['flashid']]=$falshval;
}
}
$_M[lang]=$lang;
@extract($settings);
可見,這里執行了這條SQL語句SELECT * FROM $met_config WHERE lang='$lang' or lang='metinfo',然后將結果extract到上下文中。
而$met_config這個表,格式就是utf8_general_ci,大小寫不敏感。
所以,我只需要傳入index.php?lang=Cn,在執行上述SQL語句的時候,不影響SQL語句的執行結果;而在進行if($lang==$met_index_type)...比較的時候,Cn != cn,成功進入else語句。
最后,構造下面數據包,注入獲取結果:
0x05. 漏洞利用條件
主要條件就是,需要管理員開啟偽靜態:
沒有什么其他條件了,無需登錄即可觸發。
本文由 Seebug Paper 發布,如需轉載請注明來源。本文地址:http://www.bjnorthway.com/371/
暫無評論