DeDecms任意用户登录,管理员密码重置漏洞

基本信息

漏洞编号:SSV-97087 实验版本:dedecms v5.7SP2
下载地址:http://updatenew.dedecms.com/base-v57/package/DedeCMS-V5.7-UTF8-SP2.tar.gz
参考文章:https://www.seebug.org/vuldb/ssvid-97074

前置知识

如果要把一个key存储到cookie中,织梦的存储方式是把key加密key同时存储在cookie中。每次读取用户传来的key时,将key进行加密后与传来的加密key比对,如果相同则认为这个key合法。

注意!!!如果我们能模拟出这个加密算法,我们将可以从cookie中传递任何key。或者将想要的key传给服务器,让服务器加密后拿来用。这次的漏洞就是需要一个00001的cookie值,所以我们注册一个用户名为00001账号就可以获奖00001加密后的值。

漏洞思路

  1. 首先我们需要知道管理员admin账号信息存在两个表中:管理员账号表dede_admin和会员账号表dede_member。两张表中密码相同。
  2. 我们通过修改cookie中的mid(用户id)来登录其他账号,本次就是将mid修改为1来登录admin的前台。
  3. 然后前台处可以修改密码,此处修改会将两个表dede_admindede_member中的密码同时修改。

Q:修改密码时需要原密码,怎么获得原密码?

A:原密码可以通过利用前台任意密码修改漏洞获取。

Q:前台任意密码修改漏洞不会同时修改dede_admindede_member两个表的密码吗?

A:不会,此漏洞只会修改dede_member表的密码。

Q:既然可以获取admin的前台密码,为什么不直接登录然后修改密码?

A:admin是不能直接登录前台的。

前台任意密码修改漏洞链接:

https://www.cnblogs.com/masses0x1/articles/14433241.html

漏洞分析

首先我们来看一下是怎么写cookie的:

文件位置:dedecms/include/helpers/cookie.helper.php,21行。

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);
    }
}

我们再看一下$cfg_cookie_encode变量:

$cfg_cookie_encode = '~cookieEncode~';

假如我们存个mid=1,那么cookie会存两个变量:分别为mid=1mid_ckMd5=fd3b4f2a75d4965a

其中fd3b4f2a75d4965a是通过函数substr(md5(~cookieEncode~1),0,16)得到。

然后我们再看一下cookie是怎么获取的:

文件位置:dedecms/include/helpers/cookie.helper.php,54行。

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];
            }
        }
    }
}

只有keykey__ckMd5相对应才能获取cookie。

下面我们来看看怎么获取1对应的__ckMd5,下面是访问dedecms/member/index.php时提交的cookie:

Cookie: PHPSESSID=ddafb3894d16a96197233b6ab28c98ba; 
last_vtime=1614052745;
last_vtime__ckMd5=6cc13992c8ce524d; 
last_vid=00001; last_vid__ckMd5=6d9d0779185557cd; 
_csrf_name_66650920=cc35349958296fded35969877cf44b84;
_csrf_name_66650920__ckMd5=d389d371487e2e14; DedeUserID=5;
DedeUserID__ckMd5=d1ff790bc9a1a796; DedeLoginTime=1614086248;
DedeLoginTime__ckMd5=a041486d5d63ce4d

以上cookie我们可控的只有last_vid,那我们怎样控制last_vid呢?

再看看关于last_vid的代码:

文件位置:dedecms/member/index.php,123行

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。所以我们只要用一个名为1的用户登录,last_vid就会保存1,last_vid__ckMd5保存的数据就是1对应的__ckMd5

最后我们看一下登录代码:

文件位置:dedecms/include/memberlogin.class.php,161行

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}'");
                    }
                }
            }
            #/aip}}

            //间隔一小时更新一次用户登录时间
            if(time() - $this->M_LoginTime > 3600)
            {
                $dsql->ExecuteNoneQuery("update `#@__member` set logintime='".time()."',loginip='".GetIP()."' where mid='".$this->fields['mid']."';");
                PutCookie("DedeLoginTime",time(),$this->M_KeepTime);
            }
            $this->M_LoginID = $this->fields['userid'];
            $this->M_MbType = $this->fields['mtype'];
            $this->M_Money = $this->fields['money'];
            $this->M_UserName = FormatUsername($this->fields['uname']);
            $this->M_Scores = $this->fields['scores'];
            $this->M_Face = $this->fields['face'];
            $this->M_Rank = $this->fields['rank'];
            $this->M_Spacesta = $this->fields['spacesta'];
            $sql = "Select titles From #@__scores where integral<={$this->fields['scores']} order by integral desc";
            $scrow = $dsql->GetOne($sql);
            $this->fields['honor'] = $scrow['titles'];
            $this->M_Honor = $this->fields['honor'];
            if($this->fields['matt']==10) $this->isAdmin = TRUE;
            $this->M_UpTime = $this->fields['uptime'];
            $this->M_ExpTime = $this->fields['exptime'];
            $this->M_JoinTime = MyDate('Y-m-d',$this->fields['jointime']);
            if($this->M_Rank>10 && $this->M_UpTime>0){
                $this->M_HasDay = $this->Judgemember();
            }
            if( !$formcache )
            {
                SetCache($this->memberCache, $this->M_ID, $this->fields, 1800);
            }
        }else{
            $this->ResetUser();
        }
    }
}

我们只要修改DedeUserIDDedeUserID__ckMd5为1和1对应的__ckMd5就可以用admin登录前台。

再看一下上面代码的这一句:

$this->M_ID = intval($this->M_ID);

所以这个DedeUserID不一定要为1,也可以为01,0001等等,是要intval(DedeUserID)==1即可。

漏洞复现

1.下载dedemcs v5.7SP2并开启会员模块

此处不再复述环境搭建,可参考https://www.cnblogs.com/masses0x1/articles/14433241.html

5.7版本首页打开缓慢,可删除广告模块和生成静态页面来提高速度。

在这里插入图片描述

2.注册会员

注册会员名为00001。会员名不一定要为00001,会员名通过intval()函数转化后等于1就可以。

在这里插入图片描述

3.登录,查看last_vid和last_vid_ckMd5

需访问uploads/member/index.php?uid=00001才能生成last_vid。记住last_vidlast_vid_cMd5的值。

此处用的cookie管理插件是“Cookie Quick Manager”。

在这里插入图片描述

4.修改cookie中的DedeUserIDDedeUserID__ckMd5

分别将last_vidlast_vid_ckMd5的值赋给cookie中的DedeUserIDDedeUserID_ckMd5

在这里插入图片描述

5.登录成功

在这里插入图片描述

6.修改密码

此处修改密码需要原密码,原密码的获取方法可通过另一个漏洞获取,链接如下:

https://www.cnblogs.com/masses0x1/articles/14433241.html

在这里插入图片描述

到此已获取后台密码,感谢师傅们阅读!

  • 0
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

rpsate

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值