DedeCMS最近又有一个缺陷被爆出来,可以绕过一些判断条件从而导致前台任意用户登录,配合上一个重置密码漏洞,可以达到从前台登录管理员账户并修改dede_admin表里的密码,也就是真正修改了管理员密码。下面来简单分析一下。
0x01 概述
前几天爆出的DedeCMS最新版(20180109)任意用户密码修改漏洞存在有一定局限性,一是只能影响没有设置密保问题的用户,二是不能重置管理员admin的密码,原因当时也说了,管理员信息存在另一个表dede_admin中,而且管理员默认不允许从前台登录,所以就算更改了dede_member里admin的密码也没法登录。但是最近又有一个缺陷被爆出来,可以绕过一些判断条件从而导致前台任意用户登录,配合上一个重置密码漏洞,可以达到从前台登录管理员账户并修改dede_admin表里的密码,也就是真正修改了管理员密码。
下面来简单分析一下
0x02 漏洞分析
先来看一下DedeCMS判断登录用户的逻辑
include/memberlogin.class.php:292
function IsLogin()
{
    if($this->M_ID > 0) return TRUE;
    else return FALSE;
}
跟进$this->M_ID看一下,170行
$this->M_ID = $this->GetNum(GetCookie("DedeUserID"));
GetNum()include/memberlogin.class.php:398
/**
*  获取整数值
*
* @access    public
* @param     string  $fnum  处理的数值
* @return    string
*/
function GetNum($fnum){
    $fnum = preg_replace("/[^0-9\.]/", '', $fnum);
    return $fnum;
}
正则匹配,去除了数字以外的字符,这里就可以构造一个利用点,一会儿再看
看一下GetCookie()
include/helpers/cookie.helper.php:54

关键点在这个判断条件
if($_COOKIE[$key.'__ckMd5'] != substr(md5($cfg_cookie_encode.$_COOKIE[$key]),0,16))
就是说从cookie中取到DedeUserID__ckMd5值,与md5($cfg_cookie_encode.$_COOKIE[$key])取前16位比较,相等才能进行下一步
我们知道admin的DedeUserID为1,现在需要知道DedeUserID__ckMd5的值
其实再思考一下,就算我们不知道admin的DedeUserID__ckMd5值,只要能过这个if条件就能绕过接着往下走了,那我们可不可以利用其他用户来绕过if条件呢?
在本程序中从数据库取用户的过程其实很简单,就是简单的查询语句Select * From #@__member where mid='$mid'。当我们利用其他用户的cookie通过了上面的if判断,然后修改mid为admin的id(1),就可以从前台登录到admin账户。
那么如何在请求过程中修改DedeUserID的值让它能和admin的id相等呢?
利用点一
我们使进入GetNum方法的参数为数字1+字母的形式,经过正则替换就会变成1,也就是$this->M_ID的值,然后带入数据库查询

$fnum为1qqqq的情况,经过正则替换后值成为了1
利用点二
在include/memberlogin.class.php:178有这么一行代码
$this->M_ID = intval($this->M_ID);
对$this->M_ID进行了整数类型转换,假设注册一个用户名,经过intval转换后为1就能使查询条件变成Select * From #@__member where mid='1',也就取出了管理员在dede_member表里的密码,此时配合上一个漏洞,我们已经修改了dede_member中管理员的密码,只要在前台再进行一次修改密码操作,就能真正修改admin的密码。

这是调试的时候注册用户名为0000001的情况,经过intval转换后M_ID的值变成了1
下面看一下如何从前台登录admin账户
在index.php里有一个最近访客记录的功能,
else
{
    require_once(DEDEMEMBER.'/inc/config_space.php');
    if($action == '')
    {
        include_once(DEDEINC."/channelunit.func.php");
        $dpl = new DedeTemplate();
        $tplfile = DEDEMEMBER."/space/{$_vars['spacestyle']}/index.htm";
        //更新最近访客记录及站点统计记录
        $vtime = time();
        $last_vtime = GetCookie('last_vtime');
        $last_vid = GetCookie('last_vid');
        if(empty($last_vtime))
        {
            $last_vtime = 0;
        }
        if($vtime - $last_vtime > 3600 || !preg_match('#,'.$uid.',#i', ','.$last_vid.',') )
        {
            if($last_vid!='')
            {
                $last_vids = explode(',',$last_vid);
                $i = 0;
                $last_vid = $uid;
                foreach($last_vids as $lsid)
                {
                    if($i>10)
                    {
                        break;
                    }
                    else if($lsid != $uid)
                    {
                        $i++;
                        $last_vid .= ','.$last_vid;
                    }
                }
            }
            else
            {
                $last_vid = $uid;
            }
            PutCookie('last_vtime', $vtime, 3600*24, '/');
            PutCookie('last_vid', $last_vid, 3600*24, '/');
else条件是当访问页面http://127.0.0.1/dedecms/uploads/member/index.php?uid=1111传入的uid不为空时进入
当我们传入的last_vid为空的时候,$last_vid = $uid;而uid是我们能控制的,所以我们就能控制传给PutCookie的参数,进入PutCookie方法
if ( ! function_exists('PutCookie'))
{
    function PutCookie($key, $value, $kptime=0, $pa="/")
    {
        global $cfg_cookie_encode,$cfg_domain_cookie;
        setcookie($key, $value, time()+$kptime, $pa,$cfg_domain_cookie);
        setcookie($key.'__ckMd5', substr(md5($cfg_cookie_encode.$value),0,16), time()+$kptime, $pa,$cfg_domain_cookie);
    }
}
在这里设置了last_vid__ckMd5的值
所以攻击流程已经明确了
- 注册一个普通用户,用户名满足数字1+字母的形式,或者经过intval()后值为1
- 访问用户主页,记录cookie中last_vid__ckMd5的值
- 访问index页面,替换cookie中DedeUserID和DedeUserID__ckMd5的值,替换成我们注册的用户名和last_vid__ckMd5,就能登录到前台admin
0x03 漏洞利用
- 前台注册普通用户,这里注册一个1qqqq
- 访问/member/index.php?uid=1qqqq,获取last_vid__ckMd5的值
  
- 访问/member/index.php,替换DedeUserID和DedeUserID__ckMd5的值
  
 可以发现以admin身份成功登录到了前台
  
- 同样的,修改密码访问member/edit_baseinfo.php,还是要修改cookie值
  
 原登录密码就是我们利用上一个漏洞修改的密码,也就是dede_member表中的admin密码,这样就达到了真正修改admin的密码
  
 更新数据库的时候判断如果是管理员,就更新admin表中的数据
0x04 总结
这回有两处可导致判断条件的绕过,有时候一个漏洞影响力有限的时候也不能轻视,往往配合另一处缺陷就可以造成很大的危害
0x05 防护
暂时关闭会员注册功能,管理员设置安全问题,关注官方更新补丁及时升级
