• 【代码审计】DedeCMS---任意前台用户登录(cookie伪造)


    漏洞触发点在include/memberlogin.class.php中的MemberLogin类中的登录校验函数

    //php5构造函数
        function __construct($kptime = -1, $cache=FALSE)
        {
            global $dsql;
            if($kptime==-1){
                $this->M_KeepTime = 3600 * 24 * 7;
            }else{
                $this->M_KeepTime = $kptime;
            }
            $formcache = FALSE;
            $this->M_ID = $this->GetNum(GetCookie("DedeUserID"));
            $this->M_LoginTime = GetCookie("DedeLoginTime");
            $this->fields = array();
            $this->isAdmin = FALSE;
            if(empty($this->M_ID))
            {
                $this->ResetUser();
            }else{
                $this->M_ID = intval($this->M_ID);
                
                if ($cache)
                {
                    $this->fields = GetCache($this->memberCache, $this->M_ID);
                    if( empty($this->fields) )
                    {
                        $this->fields = $dsql->GetOne("Select * From `#@__member` where mid='{$this->M_ID}' ");
                    } else {
                        $formcache = TRUE;
                    }
                } else {
                    $this->fields = $dsql->GetOne("Select * From `#@__member` where mid='{$this->M_ID}' ");
                }
                    
                if(is_array($this->fields)){
                    #api{{
                    if(defined('UC_API') && @include_once DEDEROOT.'/uc_client/client.php')
                    {
                        if($data = uc_get_user($this->fields['userid']))
                        {
                            if(uc_check_avatar($data[0]) && !strstr($this->fields['face'],UC_API))
                            {
                                $this->fields['face'] = UC_API.'/avatar.php?uid='.$data[0].'&size=middle';
                                $dsql->ExecuteNoneQuery("UPDATE `#@__member` SET `face`='".$this->fields['face']."' WHERE `mid`='{$this->M_ID}'");
                            }
                        }
                    }

    可以看到M_ID参数是由GetNum(GetCookie("DedeUserId"))赋值的。

    之后通过intval()函数对M_ID进行整数转换便没有再进行任何处理和过滤,然后通过SQL语句Select * From '#@__member' where mid='{$this->M_ID}'查询来判断用户的身份。

    因此我们跟进GetCookie()函数和GETNum()函数,查看是否可以通过修改M_ID参数来尝试是否可以进行越权。

    /**
     *  获取Cookie记录
     *
     * @param     $key   键名
     * @return    string
     */
    if ( ! function_exists('GetCookie'))
    {
        function GetCookie($key)
        {
            global $cfg_cookie_encode;
            if( !isset($_COOKIE[$key]) || !isset($_COOKIE[$key.'__ckMd5']) )
            {
                return '';
            }
            else
            {
                if($_COOKIE[$key.'__ckMd5']!=substr(md5($cfg_cookie_encode.$_COOKIE[$key]),0,16))
                {
                    return '';
                }
                else
                {
                    return $_COOKIE[$key];
                }
            }
        }
    }
        /**
         *  获取整数值
         *
         * @access    public
         * @param     string  $fnum  处理的数值
         * @return    string
         */
        function GetNum($fnum){
            $fnum = preg_replace("/[^0-9.]/", '', $fnum);
            return $fnum;
        }

    根据传入的字符串,找到cookie里相应的值,但这里对$_COOKIE[$key.'__ckMd5']进行了校验,使用的$ cfg_cookie_encode是在程序安装时设置的。

    所以如果我们不知道这个值,我们是无法破解$key.'__ckMd5'。所以一般情况下,我们是无法破解$key.'__ckMd5'这类值的。

     GetNum()函数则是对GetCookie()函数返回的$_COOKIE[$key]值进行处理,获取整数值。

    正常情况下,程序中的DedeUserID就是当前用户的用户ID。但是如果将其中的DedeUserID和DedeUserID__ckMd5都替换为其他的账户,是否就意味着我们可以以其他的账户登录呢?

    那么现在的问题就在于,如何进行伪造呢?我们知道管理员admin的mid是1,但是我们如何得到对应的DedeUserID__ckMd5呢?所以目前的问题转变为我们需要得到1所对应的DedeUserID__ckMd5的。

    漏洞利用点:

    在文件/menber/index.php中,找到这么一段代码

    /*-----------------------------
    //会员空间主页
    function space_index(){  }
    ------------------------------*/
    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, '/');

    可以看出$last_vid为空则把$uid赋值给$last_vid,而这个$uid就是可控的用户名,再通过PutCookie()函数写到cookie中。

    这里加入构造出类似0000001或1abcde这样的用户名,用我last_vid_ckMd5的值 == ($cfg_cookie_encode.$_COOKIE['last_vie'])的MD5的前16位,满足条件。

    接着在登录类的构造函数中,mid经GetNum()和intval()函数的过滤,就形成了1,接着进入数据库查询再展示到页面。

    在用户空间主页:

    http://localhost/DedeCMS/member/index.php?uid=001

    这里代码根据用户提交的参数uid调用了Getcookie()函数生成了cookie值即相应的$_COOKIE[$key]和$_COOKIE[$key.'__ckMd5']值。

    由于GetNum()函数的处理,我们在复现的时候注册攻击账号时应使用001、0001、00001之类的用户名来绕过该函数,从而达到以管理员的身份登录前台。

     ---V5.7_UTF8_SP1、SP2---

  • 相关阅读:
    Spring mvc时间格式处理
    dubbo升级spring4与cxf
    dom4j使用总结
    java utils
    ES6
    ES6
    javascript常用方法
    ES6
    ES6
    ES6
  • 原文地址:https://www.cnblogs.com/fox-yu/p/8628345.html
Copyright © 2020-2023  润新知