这套CMS是上个月中作的审计,总共找到几个后台漏洞,可后台getshell,一个逻辑漏洞可任意发短信,还有一个前台注入漏洞。不过发到了某平台上以后,审核又要求我提交利用的poc,因此懒得发去了,就这里发来,刚才看了下最新版,已经修复了。版本是74cms_v3.6_20150902。php
第一个逻辑漏洞是短信发送处的问题,在ajax_user.php文件中,原代码片断以下:ajax
$mobile=trim($_POST['mobile']); $sms_type=$_POST['sms_type']?$_POST['sms_type']:"reg"; if (empty($mobile) || !preg_match("/^(13|15|14|17|18)\d{9}$/",$mobile)) { exit("手机号错误"); } $rand=mt_rand(100000, 999999); switch ($sms_type) { case 'reg': $sms_str="您正在注册{$_CFG['site_name']}的会员,手机验证码为:{$rand},此验证码有效期为10分钟"; break; case 'getpass': $sms_str="您正在找回{$_CFG['site_name']}的会员密码,手机验证码为:{$rand},此验证码有效期为10分钟"; break; } if($_SESSION['verify_mobile']==$mobile && time()<$_SESSION['send_time']+180) { exit("180秒内仅能获取一次短信验证码,请稍后重试"); } else { $r=send_sms($mobile,$sms_str); } if ($r=="success"){ $_SESSION['mobile_rand']=substr(md5($rand), 8,16); $_SESSION['send_time']=time(); $_SESSION['verify_mobile']=$mobile; exit("success"); } else { exit("SMS配置出错,请联系网站管理员"); }
能够看出,这里每次会把此次输入的手机号和session中记录的手机号作验证,若是相同而且时间间隔不够3分钟,那么就会报错,可是这里有一个问题,就是每次输入了手机号后若是成功发送了短信,那么就会覆盖掉session中的手机号,因此咱们只须要轮询两个正常的手机号便可越过限制。目前版本已经修复,修复方法也很简单,就是先作一个时间验证便可,无论输入的手机号是多少,发短信的时间间隔不可少于60s,很聪明的fix。
后台还有一个任意文件删除的问题,文件是admin/admin_article.php,这里会删除掉$_GET来的img指向的文件,代码片断以下:sql
$id=intval($_GET['id']); $img=$_GET['img']; $img=str_replace("../","***",$img); $sql="update ".table('article')." set Small_img='' where id=".$id." LIMIT 1"; $db->query($sql); @unlink($upfiles_dir.$img); @unlink($thumb_dir.$img);
看到这里过滤了../,然而在windows下使用..\也是能够的,因此能够任意删除文件。shell
后台GetShell的方法很多,这里算是其中一个,并且估计不会在短时间内修补,由于算是一个正常的系统功能。
这个GetShell的方法很简单,就是先经过普通会员的头像文件上传,传上来有要执行的代码的图片,后台在包含进来便可。
思路就是这样,那么前台头像上传必需要没有验证上传的文件内容,看代码:windows
$savePath = "../../data/avatar/100/"; //图片存储路径 $savePathThumb = "../../data/avatar/48/"; //图片存储路径 $savePicName = time();//图片存储名称 $file_src = $savePath.$savePicName."_src.jpg"; $filename150 = $savePath.$savePicName.".jpg"; $filename50 = $savePathThumb.$savePicName.".jpg"; $src=base64_decode($_POST['pic']); $pic1=base64_decode($_POST['pic1']); $pic2=base64_decode($_POST['pic2']); if($src) { file_put_contents($file_src,$src); } file_put_contents($filename150,$pic1);
这里直接fileput_contents了,不过惋惜没办法控制文件名,通常来讲这里就没啥用了,不事后台的一个任意文件包含就能够用上了。
代码以下:session
if (!empty($crons)) { if (!file_exists(QISHI_ROOT_PATH."include/crons/".$crons['filename'])) { adminmsg("任务文件 {$crons['filename']} 不存在!",0); } require_once(QISHI_ROOT_PATH."include/crons/".$crons['filename']); adminmsg("执行成功!",2); }
这个文件是在后台的计划任务管理那里,具体名字我也忘了……可是这个部分的目的是为了帮助管理员执行一些简单的任务,好比统计站点访问的状况或者其余的自定义代码,而这里的问题就是为了方便的作到统计的目的,这套cms在common.inc.php(具体名字忘了)里include了执行计划的代码,也就是说,只要后台添加了一个计划任务,前台便可执行。验证方法也很简单,这里你们能够作一个phpinfo的文件添加到计划任务,再访问下主页便可看见主页上用户登陆处出现了phpinfo的结果。
因为这个计划任务的功能是系统功能的一部分,估计不会作啥修复,不过前台头像上传可能作一些修改,可是也不怕,这里其实还有一个问题。就是CSRF,虽然他有一个防CSRF的功能,可是他在后台居然有个能够关闭CSRF的开关,那么出于方便或者啥的缘由,若是管理员关闭了CSRF,那么只要管理员看见了一个精心构造的图片便可getshell(不过这可能性很低)。函数
这里只找到了一个前台注入漏洞,不过当时经过了官方DEMO站的验证。
文件位置: ajax_user.php,当act为get_pass_check时,出现了注入,代码以下:测试
require_once(QISHI_ROOT_PATH.'include/fun_user.php'); $username=$_POST['username']?trim($_POST['username']):exit("false"); if (preg_match("/^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/",$username)) { $usinfo=get_user_inemail($username); } elseif (preg_match("/^(13|14|15|18|17)\d{9}$/",$username)) { $usinfo=get_user_inmobile($username); } else { $usinfo=get_user_inusername($username); }
这里没有对$username作过滤,不过全局有addslash,可是这里能够宽字节注入,那么就能够注入了。DEMO站示意图:
正常请求:
这里反回了false,由于不存在这个用户,那么换个payload:网站
sunrain%df%27%20or%20%df%271%df%27=%df%271
这句话就是简单地or 1=1,若是能够注入,那么应该是返回true的:
固然,这里也是能够出数据的,当时本机测试了,可是没截图。
后台注入仍是很多,这里列出两个:
第一处: admin_category.php文件,当act是edit_color_save时,直接执行了以下语句:ui
$info=get_color_one($_POST['id']);
跟入函数:
function get_color_one($id) { global $db; $sql = "select * from ".table('color')." WHERE id=".$id.""; return $db->getone($sql); }
发现这里没有引号,因此能够注入:
第二处: 在admin_baiduxml.php文件中,当act是setsave时,执行了以下语句:
foreach($_POST as $k => $v) { !$db->query("UPDATE ".table('baiduxml')." SET value='{$v}' WHERE name='{$k}'")?adminmsg('保存失败', 1):""; }
因此注入就很明显了: