这个漏洞的前提是需要开启用户注册的功能,造成的危害是能够修改前台部分用户的密码,这部分用户是那些没有设置密保问题的用户。前台管理员密码虽然也没有设置密保问题,但是由于dedecms本身的功能即使修改密码也是无法登录的。
dedecms重置密码的原理是给重置密码的用户发送一个重置密码的链接。那么在进行重置密码时,修改为其他的用户就能够修改其他用户的密码了,所以本质上来说这是一个越权漏洞。
假设dedecms已经开启了用户注册的功能,用户重置密码的的URL是为http://localhost/member/resetpassword.php
。对应于源码的位置是member/resetpassword.php
。
在member/resetpassword.php
中存在三个重置密码的方法,分别是getped
、safequestion
、getpasswd
。而本次的漏洞与safequestion
有关。
分析safequestion
方法代码:
其中有几个关键的验证,如empty($safequestion)
、empty($safeanswer)
、if($row['safequestion'] == $safequestion && $row['safeanswer'] == $safeanswer)
。首先我们需要知道,一个没有设置密保问题的用户,默认的safequestion
和safeanswer
结果。
默认情况下的safequestion
为0,safeanswer
为空。那么如何通过这个验证呢?如果我们传输的safequestion
为0,而empty($safequestion)
为True。此时我们需要利用到php的隐式类型转换,即PHP在进行类型比较、类型判断时会自动进行一些类型的转换,如下:
所以我们设置我们输入的safequestion为0.0,safeanswer为空就可以绕过这个验证,此时empty($safeanswer)为True
,$row['safequestion'] == $safequestion也为True
,而数据中查询出来的safeanswer
本身就为NULL,所以我们设置为空,就可以通过验证。之后程序进入sn($mid, $row['userid'], $row['email'], 'N');
中。
所以这个地方重置的仅仅是那些没有设置密保问题的用户,因为只有这些用户他们的safequestion才是空
跟踪进入到member/inc/inc_pwd_functions.php:sn()
中,
进入到sn()函数之后,会执行SELECT * FROM #@__pwd_tmp WHERE mid = '$mid'
,此条SQL语句查询的是dede_pwd_tmp
,此表存储的就是重置密码的临时KEY。由于此时没有重置密码,所以没有对应此用户的记录。
进入到第一个判断newmail($mid,$userid,$mailto,'INSERT',$send);
中
追踪进入到member/inc/inc_pwd_functions.php:newmail()
中
进入到$type == 'INSERT'
中,生成一个临时KEY,$key = md5($randval);
,然后插入到数据库中,$sql = "INSERT INTO #@__pwd_tmp (mid ,membername ,pwd ,mailtime)VALUES ('$mid', '$userid', '$key', '$mailtime');";
。接下来根据参数$send
的值判断是将重置密码的链接通过邮箱发送还是直接跳转。这个参数最开始是在$dopost == "safequestion"
中设置的,默认值是N
,那么就会将对应id的密码返回。
拿到重置链接直接在浏览器中访问就可以修改此id对应用户的密码了。
重置密码发送的请求如下:
其中的pwd
和pwdok
是我设置的重置密码。
此重置密码的请求就会进入到member/resetpassword.php
的$dopost == "getpasswd"
中
进入之后,会进行$db->GetOne("SELECT * FROM #@__pwd_tmp WHERE mid = '$mid'")
查选,因为在重置密码时已经在dede_pwd_tmp
表中保存了记录,所以此时存在数据。
之后根据step的值为2,进入到更改密码的操作中。更改密码之后会进行$sn = md5(trim($pwdtmp));if($row['pwd'] == $sn
操作,与数据库的中密码进行校验,校验成功之后,就会执行一下的两条SQL语句:
至此就完整了整个任意用户密码的重置过程。
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)