DeDecms(织梦CMS) V5.7.72任意用户密码重置漏洞复现

前言

好几天没审计了,就来复现一下这个cms,难度也不是太高。

漏洞攻击流程

1:首先利用弱类型进入密码重置

http://127.0.0.1/dedecms/member/resetpassword.php?dopost=safequestion&safequestion=0.0&safeanswer=&id=1

admin默认mid是1

2:通过抓包获取到 key 值

3:去掉多余的字符访问修改密码链接

dedecms/member/resetpassword.php?dopost=getpasswd&id=1&key=xxxxx

这样我们就跳转得到了密码重置页面

代码分析

环境DeDecms(织梦CMS) V5.7.72+php7.2+apache
漏洞文件入口:

member/resetpassword.php 文件

这里是一个重新设置登陆密码功能,我们使用的是使用安全问题找回密码:

$dopost == "safequestion"

通过传入的 $mid 对应的 id 值来查询对应用户的安全问题
这里的大问题就是使用==判断安全问题和安全问题答案,这就给了我们利用弱类型绕过的可能,我们就可以绕过验证进行下一步的密码重置
怎么绕过呢?

假设用户没有设置安全问题和答案,那么默认情况下安全问题的值为 0 ,答案的值为 null (这里是数据库中的值,即 $row['safequestion']="0" 、 $row['safeanswer']=null )。当没有设置 safequestion 和 safeanswer 的值时,它们的值均为空字符串。第11行的if表达式也就变成了 if('0' == '' && null == '') ,即 if(false && true) ,所以我们只要让表达式 $row['safequestion'] == $safequestion 为 true 即可。下图是 null == '' 的判断结果:

我们可以利用 php弱类型 的特点,来绕过这里 $row['safequestion'] == $safequestion 的判断,通过测试找到了三个的payload,分别是 0.0 、 0. 、 0e1 ,这三种类型payload均能使得 $row['safequestion'] == $safequestion 为 true ,即成功进入 sn 函数。

默认状态下,临时密码的表为空,如下

mysql> select * from dede_pwd_tmp;
Empty set (0.00 sec

当我们访问完payload时,
链接会自动跳转到http://localhost/dedecms/member/resetpassword.php?dopost=getpasswd&id=1&key=xxxx

key的值就是admin的临时密码,这时再查询数据库的临时密码表,你会发现多了一条admin的记录
这也是为什么
执行 SELECT * FROM #@__pwd_tmp WHERE mid = '$mid' 语句时,返回结果为空
我们看到继续执行了newmail函数执行插入操作:

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."
Reply-To: $cfg_adminemail";
    $mailbody = "亲爱的".$userid.":
您好!感谢您使用".$cfg_webname."网。
".$cfg_webname."应您的要求,重新设置密码:(注:如果您没有提出申请,请检查您的信息是否泄漏。)
本次临时登陆密码为:".$randval." 请于三天内登陆下面网址确认修改。
".$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);
            }

该代码主要功能是发送邮件至相关邮箱,并且插入一条记录至 dede_pwd_tmp 表中。而恰好漏洞的触发点就在这里,我们看看 第13行 至 第18行 的代码,如果 ($send == 'N') 这个条件为真,通过 ShowMsg 打印出修改密码功能的链接。 第17行 修改密码链接中的 $mid 参数对应的值是用户id,而 $randval 是在第一次 insert 操作的时候将其 md5 加密之后插入到 dede_pwd_tmp 表中,并且在这里已经直接回显给用户。那么这里拼接的url其实是

http://127.0.0.1/member/resetpassword.php?dopost=getpasswd&id=$mid&key=$randval
也就是说这里的id,key我们可控

继续跟进一下 dopost=getpasswd 的操作,相关代码位置在 member/resetpassword.php 中,

我们先从96行看,dopost=getpasswd时
会先判断id是否为空
如果不为空,进入
$row = $db->GetOne("SELECT * FROM #@__pwd_tmp WHERE mid = '$mid'");

这里如果$row为空,就会退出,但是我们第一步payload使得mid已经在临时密码表中存在了,所以一定不为空,继续执行:
判断时间是否超时,如果没有,进入

templets/resetpassword2.htm

我们去看看

这里密码修改页面会将 $setp 赋值为2

由于现在的数据包中 $setp=2 ,因此这部分功能代码实现又回到了 member/resetpassword.php 文件中。

从临时密码表中取出来key和我们前端url中的key对比,相等执行密码修改。
到此,这就是整个漏洞利用过程。

攻击过程

这里我们使用test账户重置admin密码
开始也提过了
1:首先利用弱类型进入密码重置

http://127.0.0.1/dedecms/member/resetpassword.php?dopost=safequestion&safequestion=0.0&safeanswer=&id=1

admin默认mid是1

2:通过抓包获取到 key 值:key=KUvaNLpw
此时临时密码表中有了我们的key:

3:去掉多余的字符访问修改密码链接

dedecms/member/resetpassword.php?dopost=getpasswd&id=1&key=KUvaNLpw

这样我们就跳转得到了密码重置页面

PS

这个漏洞利用还有一个条件:

网站开启了会员注册功能,如果不知道如何开启的话,可以按照下图进行开启测试

这里我一开始不知道。。导致耽误了一会时间。‘
还有就是一开始不知道默认后台是dede。。使用了爆破的方法。。

从网上直接找的dedecms检测插件也能检测出来漏洞:

这里看到有getsghell漏洞,确实存在不过不是这个
是后台存在多处fwrite函数导致getshellgetshell

这里这个不是我们的重点简单提一句。。

参考

https://github.com/hongriSec/PHP-Audit-Labs/blob/master/Part1/Day4/files/README.md

原文地址:https://www.cnblogs.com/wangtanzhi/p/12813642.html