DeDecms 任意用户密码重置漏洞POC&分析

admin 2018年5月8日13:30:03评论1,352 views字数 7169阅读23分53秒阅读模式
摘要

Dedecms是一款开源的PHP开源网站管理系统。DeDecms(织梦CMS) V5.7.72 正式版20180109 (最新版)
由于前台resetpassword.php中对接受的safeanswer参数类型比较不够严格,遭受弱类型比较攻击
导致了远程攻击者可以在前台会员中心绕过验证,进行任意用户密码重置攻击


描述

Dedecms是一款开源的PHP开源网站管理系统。

DeDecms(织梦CMS) V5.7.72 正式版20180109 (最新版)
由于前台resetpassword.php中对接受的safeanswer参数类型比较不够严格,遭受弱类型比较攻击
导致了远程攻击者可以在前台会员中心绕过验证,进行任意用户密码重置攻击

当检测到此类攻击后,迅速Neubee Lab对漏洞攻击进行了分析

漏洞触发位置

①文件位置:dedecms/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();     }  } 

就是这里的判断出现了问题,因为使用了不够严谨的 == 进行了比较,导致if语句的条件为真,就会进入分支,进入sn函数

if($row['safequestion'] == $safequestion && $row['safeanswer'] == $safeanswer) {     sn($mid, $row['userid'], $row['email'], 'N');     exit(); } 

②文件位置:dedecms/member/inc/inc_pwd_functions.php(150行)
函数名称:function sn

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

在sn函数内部,会根据id到pwd_tmp表中判断是否存在对应的临时密码记录,根据结果确定分支,走向newmail函数

    if(!is_array($row))     {         //发送新邮件;         newmail($mid,$userid,$mailto,'INSERT',$send);     } 

③文件位置:dedecms/member/inc/inc_pwd_functions.php(73行)
函数名称:function 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."/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);             }         }         else         {             return ShowMsg('对不起修改失败,请联系管理员', 'login.php');         }     }     elseif($type == 'UPDATE')     {         $key = md5($randval);         $sql = "UPDATE `#@__pwd_tmp` SET `pwd` = '$key',mailtime = '$mailtime'  WHERE `mid` ='$mid';";         if($db->ExecuteNoneQuery($sql))         {             if($send == 'Y')             {                 sendmail($mailto,$mailtitle,$mailbody,$headers);                 ShowMsg('EMAIL修改验证码已经发送到原来的邮箱请查收', 'login.php');             }             elseif($send == 'N')             {                 return ShowMsg('稍后跳转到修改页', $cfg_basehost.$cfg_memberurl."/resetpassword.php?dopost=getpasswd&id=".$mid."&key=".$randval);             }         }         else         {             ShowMsg('对不起修改失败,请与管理员联系', 'login.php');         }     } } 

进入newmail函数后,会因为$type的值进入这个分支

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);         }     }     else     {         return ShowMsg('对不起修改失败,请联系管理员', 'login.php');     } } 

然后因为$send == 'N'这个条件为真,通过ShowMsg打印出修改密码的连接,导致漏洞形成

else if ($send == 'N') {     return ShowMsg('稍后跳转到修改页', $cfg_basehost.$cfg_memberurl."/resetpassword.php?dopost=getpasswd&id=".$mid."&key=".$randval); } 

密码修改连接如下,点击后重新调到resetpassword.php这个页面

文件位置:dedecms/member/resetpassword.php(96行)

else if($dopost == "getpasswd") {     //修改密码     if(empty($id))     {         ShowMsg("对不起,请不要非法提交","login.php");         exit();     }     $mid = preg_replace("#[^0-9]#", "", $id);     $row = $db->GetOne("SELECT * FROM #@__pwd_tmp WHERE mid = '$mid'");     if(empty($row))     {         ShowMsg("对不起,请不要非法提交","login.php");         exit();     }     if(empty($setp))     {         $tptim= (60*60*24*3);         $dtime = time();         if($dtime - $tptim > $row['mailtime'])         {             $db->executenonequery("DELETE FROM `#@__pwd_tmp` WHERE `md` = '$id';");             ShowMsg("对不起,临时密码修改期限已过期","login.php");             exit();         }         require_once(dirname(__FILE__)."/templets/resetpassword2.htm");     }     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;                     }                 }             }             showmsg('对不起,新密码为空或填写不一致', '-1');             exit;         }         showmsg('对不起,临时密码错误', '-1');         exit;     } } 

在pwd_tmp表中查询判断后,最后会跳入这个分支,判断是否超时,然后进入密码修改页面

if(empty($setp)) {     $tptim= (60*60*24*3);     $dtime = time();     if($dtime - $tptim > $row['mailtime'])     {         $db->executenonequery("DELETE FROM `#@__pwd_tmp` WHERE `md` = '$id';");         ShowMsg("对不起,临时密码修改期限已过期","login.php");         exit();     }     require_once(dirname(__FILE__)."/templets/resetpassword2.htm"); } 

DeDecms 任意用户密码重置漏洞POC&分析 然后会再次进入这个页面,不过因为setp2,会进入这个分支。完成member表的密码修改

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;                 }             }         }         showmsg('对不起,新密码为空或填写不一致', '-1');         exit;     }     showmsg('对不起,临时密码错误', '-1');     exit; } 

总结

DeDecms(织梦CMS) 密码修改处,因为安全问题验证的safeanswer参数类型比较不够严格,遭受弱类型比较攻击,可绕过判断
同时修改时的id可控,导致了远程攻击者可以在前台会员中心绕过验证,进行任意用户密码重置攻击

本地验证

分别注册两个账号

账号:test1
密码:test1

账号:test2
密码:test2

目的:我们使用test1账号去重置test2账号的密码,重置为123456

①test1登录,并发送payload,此处可以id可以遍历,9是test2的id
DeDecms 任意用户密码重置漏洞POC&分析 ②我们在代理工具中找到ShowMsg打印出修改密码的连接
DeDecms 任意用户密码重置漏洞POC&分析③去掉多余字符后,访问修改密码的连接,进入修改页面

DeDecms 任意用户密码重置漏洞POC&分析④test2密码修改成功,数据库对应hash也进行了更新(变成了123456的hash)
DeDecms 任意用户密码重置漏洞POC&分析

POC

if($row['safequestion'] == $safequestion && $row['safeanswer'] == $safeanswer) {     sn($mid, $row['userid'], $row['email'], 'N');     exit(); } 

0

本文转自T00LS    作者:[ hanfengbuleng ]

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2018年5月8日13:30:03
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   DeDecms 任意用户密码重置漏洞POC&分析http://cn-sec.com/archives/51040.html

发表评论

匿名网友 填写信息