原文链接:搭建dedecms漏洞靶场练习环境
前言
本文将对dedecms(织梦cms)所存在的漏洞进行分析和复现,因为代码审计较弱,代码这一块的分析借鉴了一些大佬们的思想,在这里对大佬们表示衷心的感谢。
环境搭建下载DedeCMS源码放到phpstudy目录下
然后输入127.0.0.1/DedeCMS/uploads/install/index.php进行安装
进入环境检测页面
进行环境配置
这里先进入网站的后台
默认密码为admin admin
这里需要改一下绝对路径
系统默认管理路径是dede,登陆管理后台可以通过地址http://127.0.0.1/dede/login.php进行访问
这里我直接在根目录下更改
重新进去管理员后台即可
然后再对php文件进行修改
然后再对默认的管理员名称和密码进行修改
更改成功
对数据进行还原
看一下网站的地址
点击生成更新网站
更新后如图所示
然后即可访问网站,CMS搭建完毕
漏洞分析及复现前台任意用户密码修改漏洞成因在用户密码重置功能处,php存在弱类型比较,导致如果用户没有设置密保问题的情况下可以绕过验证密保问题,直接修改密码(管理员账户默认不设置密保问题)。值得注意的是修改的密码是member表中的密码,即使修改了管理员密码也是member表中的管理员密码,仍是无法进入管理。
漏洞代码分析php弱类型比较问题很常见,在不同类型比较时,如果使用的是==,php会将其中一个数据进行强制转换为另一个,比如'123a'就会被强制转换成123。这样就出现了弱类型比较问题,当然如果使用===判断比较就不会出现问题了,常见比较如下
- '' == 0 == false '123' == 123 //'123'强制转换为123
- 'abc' == 0 //intval('abc')==0
- '123a' == 123 //intval('123a')==123
- '1' == 1 //被识别为十六进制
- '0e123456789' == '0e987654321' //被识别为科学计数法
- [false] == [0] == [NULL] == ['']
- NULL == false == 0
- true == 1
复制代码 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');
- }
- }
复制代码 可见在sn函数中将send参数设置了'N',其实就是生成了暂时密码并插入了数据库中,并进行跳转
- else if ($send == 'N')
- {
- return ShowMsg('稍后跳转到修改页', $cfg_basehost.$cfg_memberurl."/resetpassword.php?dopost=getpasswd&id=".$mid."&key=".$randval);
- }
复制代码
跳转链接就是修改密码的链接了
漏洞复现首先打开后台管理页面开启会员功能
注册一个帐号
信息的话随便填一下即可注册成功
然后进入了个人中心
进入会员中心
点击通过安全问题取回
输入用户名
拖入bp改包为payload
dopost=safequestion&id=1&userid=admin&safequestion=00&safeanswer=0&vdcode=Vs4p
进入url,修改密码
|