DeDeCMSv5.7-前台任意用户密码修改漏洞复现

DeDeCMSv5.7-前台任意用户密码修改漏洞复现

十一月 24, 2021

影响版本

DeDeCMSv5.7 SP2正式版

漏洞限制

  1. 只影响前台账户
  2. 只对修改未设置安全问题的用户

复现环境

PHP 5.6.9

DeDeCMSV5.7SP2 正式版

复现过程

安装

按照网上的教程安装好dedecms后,需要在后台管理处打开会员的注册功能,否则无法正常打开/member下的页面。

过程

  • 先注册一个test用户,设置安全问题,发现在数据库中safequestion的值为1;id=1的前台admin用户默认safequestion值为0;注册用户text,不设置安全问题,数据库中safequestion为0。
  • 构造请求:
1
2
http://127.0.0.1/dedecms/member/resetpassword.php
post传入:dopost=safequestion&safequestion=0.0&safeanswer=&id=3
  • 利用bp抓包然后重放,得到一个链接:

img

  • 直接访问:

    1
    2
    3
    http://127.0.0.1/dedecms/member/resetpassword.php?dopost=getpasswd&id=3&key=EzL9nn7r
    将分隔符amp;删除,直接访问http://127.0.0.1/dedecms/member/resetpassword.php?dopost=getpasswd&id=3&key=EzL9nn7r

    • 访问后直接跳转到可以修改密码的界面,重设id为3用户的密码,然后利用修改后的密码成功登陆。
  • img

  • 观察数据库中,text的密码被成功修改:

img

代码分析

下面依照第一个payload进行简单分析:

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

img

1
2
3
首先是menber/resetpassword.php的75行,在重设密码的过程中,如果提交的$dopost参数等于safequestion,就会将对应用户输入的安全问题和答案跟所设置的进行对比,如果输入的问题和答案与之前设置的一致,则进入第三个if语句的内部,调用sn()函数。

因为在用户中,没有设置安全问题的用户的$safequestion的值默认为0:

img

1
当输入的$safequestion也为0 的时候,$row['safequestion'] == $safequestion成立,达到调用sn()函数的目的,代码往下执行。但是在php代码中,如果我们直接输入0,则该变量显示为空:

img

1
2
3
 变量为空的话,会使得 if(empty($safequestion)) $safequestion = '';成立,无法进入if($row['safequestion'] == $safequestion && $row['safeanswer'] == $safeanswer)这个if分支。

但是如果我们输入的是0.0,则变量不为空。

img

  • 并且$row[‘safequestion’] == $safequestion判断使用的是弱比较,弱比较中会将数字其他类型转化为int类型进行比较。(弱比较判断不细说)

img

  • 因此我们考虑让$safequestion=0.0绕过。$row[‘safequestion’] == $safequestion成立,右边$row[‘safeanswer’] == $safeanswer本就为空,不用处理。
  • 在这里我们要注意到调用sn()函数时默认的send值为N。继续跟进sn()函数,看看用法:
  • member/inc.pwd.functions.php的150行:

img

1
先查询了临时密码表,第一个if语句为判断临时密码是否存在,不存在就发送邮件,第二个语句为判断临时密码是否过期。因为我们是第一次输入密码,因此临时密码不存在,进入第一个if判断。
  • 跟进newmail()函数,

img

1
random()生成一个八位随机数,因为在sn()函数中设置了type为INSERT,因此进入第一个if判断。将随机生成数进行md5加密,把密码插入到临时密码表中。因为调用sn()函数时默认的send值为N,因此进入 else if ($send == 'N')   ,不发送邮件,直接调用ShowMsg函数,跳转到$cfg_basehost.$cfg_memberurl."/resetpassword.php?dopost=getpasswd&id=".$mid."&key=".$randval这个页面上。
  • 根据参数$mid再进行查看:跳转页面仍然是resetpassword.php,dopost=getpasswd,跟进查看:

img

1
这里再次通过mid查询了该用户的临时密码是否存在,不存在就会返回登录页面。存在则进入if(empty($setp)),第一个if先判断了临时密码时间是否为过期,不过期的话执行require_once(dirname(__FILE__)."/templets/resetpassword2.htm"); 跳转到/templets/resetpassword2.html。这个就是重置密码的页面了。

img

  • 输入密码提交后交给resetpassword.php,注意到这step值为2.

img

  • 这里是检查两次输入的新密码是否一致,一致的话将临时密码表中的密码删除,修改用户对应的密码。
  • 整个修改密码的过程结束。

参考文章

dedeCMS 5.7 任意用户密码重置漏洞 - it610.com