前言
在 SEMCMS php v2.7
审计之前,我会去看看要审计的CMS官网是否存在手册说明什么的,然后去会各个漏洞公布平台找找它以前的老漏洞,复现下是否修复及修复是否完整(主要是去看看会不会有现金奖励)。
看着看着就发现了两处问题,算是捡来的漏洞吧。
防sql正则校验不合理
去官网看了下并没有什么文档,但搜到了其在 2.1~2.3
的版本中存在后台任意用户登陆的问题,随后就去看了看。
原因是后台登陆时会最终调用 xxxx_Admin/Include/function.php -> checkuser()
方法来验证账号:
function checkuser(){ //判断账号
$cookieuser=@htmlspecialchars($_COOKIE["scuser"]);
$cookieuserqx=@htmlspecialchars($_COOKIE["scuserqx"]);
$sql="select * from sc_user where user_ps='$cookieuser' and user_qx='$cookieuserqx'";
$result=mysql_query($sql);
$row = mysql_fetch_array($result,MYSQL_ASSOC);
if (!mysql_num_rows($result)){ echo "<script language='javascript'>alert('账号密码不正确重新登陆!');top.location.href='index.html';</script>";}
else {echo'';}
}
按照文章中大佬说的方法,设置 Cookie 参数:scuser=\; scuserqx= or 1=1 #
及可实现任意用户登陆(其原理是用 \
符号转义 $cookieuser
后面的单引号闭合,再通过 $cookieuserqx
参数完成注入语句)。
select * from sc_user where user_ps='\' and user_qx=' or 1=1 #'
但在
2.3
版本以后web_inc.php
文件中引入了一个web_sql.php
的文件,用于验证全局的$_GET
和$_COOKIE
参数中是否存在注入语句:
<?php
// 防sql入注
if (isset($_GET)){$GetArray=$_GET;}else{$GetArray='';} //get
if (isset($_COOKIE)){$CookArray=$_COOKIE;}else{$CookArray='';} //cookie
foreach ($GetArray as $value) {//get
verify_str($value);
}
foreach ($CookArray as $value) { //cookie
verify_str($value);
}
function inject_check_sql($sql_str)
{
return preg_match('/select|insert|=|<|update|\'|\/\*|\*|union|into|load_file|outfile/i', $sql_str);
}
function verify_str($str)
{
if (inject_check_sql($str)) {
exit('Sorry,You do this is wrong! (.-.)');
}
return $str;
}
这里我们主要看那段正则就可以了,上面用到的 or 1=1 #
已经不行了,因为里面含有等号。
这里其实很好绕过它的检测,我们只需要稍微改动下,依然用 or
就可以再次实现任务用户登录: or -1 #
带着伪造好的 cookie
参数,直接访问后台的 SEMCMS_Main.php
地址就可以登录进后台了。
至于后台怎么找,按照生成规则爆破也行,gg语法找后台也可以。
前台一处Insert盲注
这段代码位于: Include/web_email.php
文件中,可以看到变量 $msg_tel
未过滤,然后拼接进了 insert into
的sql语句中。
因为在 db_conn.php
中关闭了错误回显,那这里只能使用时间盲注的技巧来就进行注入了。
当配置文件中开启了邮件发送
$web_mailopen
的值就变成了1,这种场景下跑盲注动静有点大哦,需要控制好请求
验证延时注入是否生效:
管理员账号密码重置【复现】
2018.06.06 晚补充
今天在看 seebug 时发现审计过的这个 CMS 系统有爆 任意密码重置漏洞
,在想当初审计时为什么没有注意到(果然还是太年轻呀…)。
首先通过关键词定位到人口文件:
$ ag -i '找回密码'
Include/web_email.php
20:// 找回密码
38: $mailcontent="网站管理员你好:<br>你的邮箱是:".$postEmail."<br> 点击<a href='".$fhurl."?umail=".$postEmail."&type=ok' target='_blank'>找回密码</a>"
定位到找回密码的代码块在 Include/web_email.php
文件中
通过图中的代码可以看到,通过接收 $_GET["type"]
参数来控制进入的 if()
流程。
当然需要找回密码时,会随机生成一个 10~10000 之间的认证码交给 $fsjs
,在通过 UPDATE
去更新 表 user_rzm
中的值。
这个时候想起该 CMS 在创建数据库的数据时,会默认写入一个管理员账号,文件在 install/semcms.sql
中有这样一段:
--
-- 转存表中的数据 `sc_user`
--
INSERT INTO `sc_user` (`ID`, `user_name`, `user_admin`, `user_ps`, `user_tel`, `user_qx`, `user_time`, `user_email`, `user_rzm`) VALUES
(3, '总账号', 'Admin', 'c4ca4238a0b923820dcc509a6f75849b', '333', '74,76,77,87,88,75,78,79,80,81,82,83,84,89,100,en', '2016-09-22 18:23:02', '41864438@qq.com', '2754');
这样我们就知道了后台管理员账号的邮箱,和初始验证码。
我们在看看重置密码的方法,在 $Type=="findok"
的时候:
当我们提交三个参数后,在 UPDATE
的 where 条件中只用到了两个,也没有做任何频率控制,通过邮箱加验证码就可以任意的重置密码了。
也没有做任何的频率控制,只要满足以下条件统统可以重置管理员密码:
- 初始
总账号
邮箱和验证码未发生过改动 - 初始验证码未发送过改动,可以通过
$Type=="fintpassword"
中的 sql 查询去爆破邮箱,然后再重置密码 - 已知邮箱但验证码发生过更改,可以通过触发
$Type=="fintpassword"
中的代码去重置验证码,然后再爆破$Type=="findok"
代码的验证实现重置密码
哈? user_name
被改了? 那就洗洗睡吧