PHP2Shell 隔离复现实验¶
本文给出 ChatBug 的完整可运行验收路径,并明确它验证什么、没有验证什么。它使用普通网站模型,不为某一个具体业务系统保留专用 CLI。
关键边界:PHP2Shell CVE 是 PHP 运行时内存安全问题。ChatBug 不向生产或 lab 发送 CVE-2026-109618/109619 的 OOM、UAF 或崩溃 payload。本页的完整 lab 验证的是另一条确定性的传统链:
session upload-progress + 应用 include gadget。两者都可能影响修补优先级,但不能相互替代或混称为同一个生产 RCE 结论。
先选择正确的证据层级¶
| 目标 | 推荐命令 | 可以得出的结论 | 不能得出的结论 |
|---|---|---|---|
| 只盘点运行时版本 | web check <URL> --skip-multipart |
HTTP/版本头暴露 | parser、gadget 或命令执行 |
| 确认普通上传入口 | web check <URL> --skip-gadget-scan |
multipart parser 可达 | LFI、PHP echo、系统命令 |
| 检查固定 session/include marker | web check <URL> |
受控 marker 路径线索 | 任意命令、用户数据读取、内存漏洞利用 |
| 回归请求/响应契约 | lab local-list |
本机正/负向协议契约 | 真实 PHP 或 shell 执行 |
| 验收固定命令输出 | lab docker-list |
runtime + lab-only gadget 可执行固定 ls/cat |
真实应用 gadget 或生产 CVE 利用 |
lab-only 链概览¶
下面的链只在 docker-list 创建的临时环境中生成:
multipart POST
└─ PHP_SESSION_UPLOAD_PROGRESS 写入 lab session 文件
└─ filename 只携带固定 allowlist proof 的 PHP 代码
└─ lab-only include($_GET['page']) 包含 session 文件
└─ 固定 ls/cat 输出经正向响应返回
真实应用缺少其中任一条件时,都不能得到相同结论:
- 没有应用 include/LFI sink,只能报告 runtime/parser 暴露;
docker exec ... php是管理面执行,不是未认证利用;- 普通 PHP 页面可以读取文件,也不代表远程攻击者能执行命令。
本机 contract lab¶
该命令在当前进程中启动绑定 127.0.0.1:0 的临时 HTTP 服务,并执行正/负向控制:
- multipart 请求被记录为合成 session 状态;
- 正向
vuln.php返回合成/tmp、chatbug-marker.txt和随机 marker; - 负向
hello.php只返回Hello World; - context 退出时关闭 socket、线程并清空内存状态。
成功 JSON 的关键形状:
{
"accepted": true,
"positive_status": 200,
"negative_status": 200,
"positive_output": "/tmp\nchatbug-marker.txt\nCHATBUG_MARKER_EXAMPLE",
"negative_body": "Hello World\n"
}
这是确定性的 contract/regression 用例,不启动真实 PHP、Docker 或系统 shell。适合 CI:
Docker 运行时 lab¶
在自有 Docker 主机上运行:
chatbug php2shell lab docker-list \
--ssh-host <owned-docker-host> \
--fpm-image <exact-runtime-image-or-digest> \
--proof list-and-marker
--ssh-host 是必填项。默认镜像 php:8.1.34-fpm-alpine 用于工作流验证;若要与被评估服务比对,必须将 --fpm-image 指向同一 PHP-FPM 运行时的精确 digest。
每次运行会在远端 <chatarch-home>/chatbug/labs/ 下创建唯一目录,并只生成:
- 指定 PHP-FPM 镜像、Nginx 和一次性 Python client;
hello.php,固定输出Hello World;vuln.php,只存在于 lab,含include($_GET['page']);- upload-progress/session 配置;
- internal-only Docker network;
/tmp/chatbug-marker.txt中的随机合成 marker。
它不会挂载生产数据库、邮件、附件、用户目录、凭据、日志或真实 session 目录,也不会发布主机端口。默认会删除 Compose 项目、网络和远端临时目录;--keep 仅用于短时 SSH 人工排查。
固定 proof 模式¶
--proof 没有任意 shell 参数,只允许:
| proof | 固定 lab 内动作 | 正向验收 |
|---|---|---|
list-tmp |
ls -la /tmp |
列表包含合成 chatbug-marker.txt |
cat-marker |
cat /tmp/chatbug-marker.txt |
输出本次随机 marker |
list-and-marker |
ls -la /tmp; echo MARKER; cat /tmp/chatbug-marker.txt |
同时输出列表、MARKER 与随机 marker |
默认的 list-and-marker 是最完整、最容易审计的验收模式。
正向与负向验收¶
list-and-marker 成功时,result.positive_output 的形式如下;ChatBug 会去掉内部定界符,只保留命令输出:
result.negative_output 必须为:
accepted=true 的条件因 proof 而异,但始终要求:
- multipart POST、正向 include 和负向普通页面均返回 200;
- 正向输出出现对应的固定证据(
list-tmp是 marker 文件名;另两种 proof 是随机 marker); - 负向输出不含随机 marker;
- 未指定
--keep时,Compose 项目和远端临时目录完成清理。
这个结果应表述为“指定 runtime + lab-only include gadget 已在隔离环境中证明”,而不是“生产 PHP2Shell RCE 已复现”。
真实网站检查¶
低影响版本/入口检查¶
这会执行版本头读取和普通 multipart parser 检查。它不发送 session include 请求,适合作为生产初始盘点。
授权范围内的 marker 探测¶
这个模式额外发送固定 <?php echo '<marker>'; ?> filename,并探测固定少量 session/include 形态。它不会执行系统命令或读取真实用户文件,但 LFI 存在时可导致该受控 echo 在目标 PHP 进程中执行。只在明确授权、日志/维护窗口合适的情况下使用。
| 字段 | 正确解释 |
|---|---|
verdict=vulnerable_version_exposed |
版本头在受影响范围;需要确认真实 HTTP SAPI 并安排修补 |
evidence_tier=tier_2_parser_reachable |
普通 multipart 请求已到达 parser 路径 |
evidence_tier=tier_3_gadget_found |
固定 marker 经候选 session/include 路径返回,需要人工复核应用 gadget |
rce_or_file_read_proven=true |
兼容字段;表示受控 marker 路径,而非任意系统命令、真实文件读取或 CVE 内存利用成功 |
复现后的处置¶
- 盘点真正服务 HTTP 请求的 PHP SAPI:FPM、mod_php、FrankenPHP、LiteSpeed 等;CLI 版本不是 Web 运行时结论。
- 升级到官方修复版本,并以运行中容器 digest、包版本或 HTTP SAPI 版本回读验证。
- 审计并移除不受信任的文件包含/路径拼接 sink。
- 收紧上传、session 保存路径、临时目录权限和错误暴露。
- 修补后先重跑低影响
web check --skip-gadget-scan,再在同镜像 data-free lab 重跑正/负向验收。 - 如怀疑已被利用,单独进行访问日志、异常 session、WebShell、子进程和出站连接取证;漏洞验证不能替代取证。