CVE-2026-87902 的检测工具包与可复现实验环境,该漏洞为未认证的 WordPress 路径遍历。包含远程检查器、IoC 分析器、Sigma 规则以及 Docker 测试平台。
用于 GHSA-7hp8-65ch-5whp / CVE-2026-87902 的检测工具与可复现测试平台 —— 页面模板解析中的未认证路径遍历导致条件性 RCE(WordPress,CWE-98,CVSS 4.0:9.2)。
check/ | 远程、被动检查器,无需服务器访问权限 |
detect/ | IoC 分析器 + Sigma 规则 |
offensive/ | 痕迹生成器,用于在真实日志上验证检测 |
docker-compose.yml + provision/ | 测试平台,三种配置 |
tests/validate.py | 质量门禁 —— 决定是否可发布 |
docs/ANALYSIS.md | 漏洞、修复补丁、实测可达性分析 |
make up # 启动测试平台 make ioc # 攻击语料 -> 日志 -> 检测
make scan # 检查测试平台 make test # 质量门禁
三个 WordPress 实例运行在 127.0.0.1 上,分别隔离影响判定结果的每个因素。
8092 最具启发性:核心与 8091 同样存在漏洞,但缺少主题前提条件。这正是说明仅凭版本进行分诊会高估暴露面的原因。主题是 8091 与 8092 之间唯一的变量,补丁是 8091 与 8093 之间唯一的变量:三者都包含相同类型的自定义内容(provision/mu-plugins/00-lab-cpt.php)以及相同的请求状态探针(10-lab-debug.php,通过 X-Lab-* 响应头暴露 is_page()、加载器所见的 pagename 值以及最终包含的模板)。
make up # 启动并配置 —— 幂等,可重复运行
make status # 每个实例的版本
make down # 停止 make clean:同时删除卷
官方 Docker 镜像将 /var/log/apache2/access.log 指向 /dev/stdout:日志输出到容器标准输出,而非文件。
docker compose logs --no-log-prefix vuln-pre # 访问 + 错误
docker compose logs --no-log-prefix vuln-pre > access.log # 用于分析
docker compose logs -f --no-log-prefix vuln-pre # 实时
make logs # 三个实例
在传统服务器上:/var/log/apache2/access.log、/var/log/nginx/access.log,或大多数共享主机上的 /home/*/logs/。日志格式必须包含查询字符串 —— %r 或 combined 格式包含它,基于 %U 构建的 LogFormat 会丢失它,而没有它就无法进行任何检测。
check/wp-ghsa-7hp8-check.py从互联网发起,无需服务器访问权限。无载荷、无路径遍历、无写入。 对每个主机:检测 WordPress、从五个来源交叉验证版本(meta generator、RSS 订阅源、wp-links-opml.php、readme.html、核心资源的 ?ver=)、活动主题,并探测 page-* 目录。
python3 check/wp-ghsa-7hp8-check.py --hosts-file hosts.txt --csv out.csv --json out.json
“受影响”意味着存在漏洞代码,而非攻击者可以执行代码。参见 docs/ANALYSIS.md。
--transport browser(默认)驱动已安装的 Google Chrome;子请求从 页面内 执行的 fetch() 发出,继承其 TLS 栈、HTTP/2 头部顺序和 Cookie,从而避免在 URL 被读取之前就被 CDN 过滤。--transport direct 仅使用标准库。--scheme http|https 避免 https → http 回退,否则会在目标日志中留下一条带有原始 TLS ClientHello 的 400 记录。
每个请求都以毫秒精度记录在 JSONL 日志中:会话标识符、请求编号、阶段、URL、状态、大小、耗时、出口 IP、标记。标记 SECAUDIT/<nonce> 同时出现在 X-Security-Audit 头部 和 User-Agent 后缀中 —— 追加,从不替换,以便在标准访问日志中保持可见而不破坏浏览器签名。可通过 --marker 自定义。
无远程预言机。
--probe-inclusion选项在惰性目标(wp-includes/version.php,已在引导时加载:require_once会使其完全成为 no-op)上执行差分比较。在标准安装上它返回NOT_REACHABLE,即使在存在漏洞的核心上也是如此,已修复与未修复实例之间的响应逐字节相同。这不是工具的限制:WordPress 在查询模板层级之前就返回 404。量化演示见docs/ANALYSIS.md第 3 节。
像 pagename=.*%2e%2e%2f 这样的字面正则可以通过改变大小写(%2E)、再编码一次(%252e)或混合字面与编码(templates/..%2f../)来绕过。任何模式列表在构造上都是不完整的。
因此我们从代码出发,而非攻击者的写法:
pagename 在到达磁盘之前最多经过两次解码 —— PHP 对查询字符串的解码,然后是 get_page_template() 中显式的 urldecode()。file_exists() 的路径必须包含 .. 组件。在 Linux 上,父目录恰好由两个字节 0x2E 0x2E 表示;在文件系统层面不存在其他表示形式。因此:解码至不动点并在每一层进行测试。这是 WordPress 所做操作的严格超集 —— 增加一层编码只会将匹配移到另一层,而我们同样会遍历该层。
python3 detect/wp-ghsa-7hp8-ioc.py access.log
docker compose logs --no-log-prefix vuln-pre | python3 detect/wp-ghsa-7hp8-ioc.py -
Sigma 规则见 detect/sigma-wp-ghsa-7hp8.yml。由于 Sigma 无法递归解码,这些规则枚举了 0 到 3 层:这是有意为之的近似,用于一级分诊。将匹配结果重新交给分析器以做判定。
WP::parse_request() 在 $_GET 之前读取 $_POST。因此 pagename 可以出现在请求体中,而不出现在任何访问日志中。需要在 WAF 或 ModSecurity 层面针对请求体进行覆盖。任何基于访问日志的规则都无法覆盖第一点。这是载体的局限,而非规则的局限 —— 但在宣称完全覆盖之前必须了解这一点。
offensive/generate-traces.py 重放同一载荷的 12 种不同写法(字面、单/双/三重编码、大小写、混合、UTF-8 超长编码、点-空格)以及 7 个与之相似的合法请求(含点的 slug、slug 中的 wp-includes、日期永久链接、百分号编码)。
它不会获得任何结果:在标准安装上,该向量不存在远程利用方式。它生成痕迹 —— 这就是它的全部目的。
make ioc # 生成语料,获取真实日志,运行分析器
预期结果,并由 make test 在真实 Apache 日志上验证:
12 个载荷全部检出(11 个 CRITICAL,超长编码为 MEDIUM,因为它在 Linux 上不可利用),7 个合法请求 0 告警,并在三个不同解码层级上有效检测。
目标仅限于本地测试平台;任何其他目标都需要 --i-have-authorization。
php.ini 中设置 register_argc_argv = Off;如不使用则卸载 PEAR —— 这是公告中提到的包含 → 执行的关键环节。open_basedir 限制在站点根目录:可约束任何本地包含。make test 是发布条件:公告中 25 个分支的矩阵(包括比较陷阱 —— 6.8.9 < 6.8.10 按数值比较、预发布版本、矩阵之外的分支)、检查器对三个实例的测试,以及在真实日志上验证的 IoC 规则。探针的 NOT_REACHABLE 断言被有意固化:如果它失效,说明行为已改变,分析必须重新进行。
仅可用于您负有责任的资产,或在书面授权下使用。测试平台仅监听 127.0.0.1;存在漏洞的实例绝不应暴露。探针 10-lab-debug.php 会泄露服务器路径:仅供测试平台使用。
| 端口 | 实例 | 核心 | 活动主题 | 预期判定 |
|---|
| 8091 | vuln-pre | 6.8.1 —— 未修复 | Twenty Twelve,含 page-templates/ | AFFECTED_PRECONDITION_MET |
| 8092 | vuln-nopre | 6.8.1 —— 未修复 | Twenty Twenty-Four,无 page-* | AFFECTED_CORE_ONLY |
| 8093 | patched | 6.8.10 —— 已修复 | Twenty Twelve,含 page-templates/ | NOT_AFFECTED |
| 判定 | 含义 |
|---|
AFFECTED_PRECONDITION_MET | 核心存在漏洞 且 活动主题上存在 page-* 目录。优先处理。 |
AFFECTED_THEME_UNKNOWN | 核心存在漏洞,主题未确定。 |
AFFECTED_CORE_ONLY | 核心存在漏洞,缺少主题前提条件。仍需修复。 |
VERSION_UNKNOWN | 检测到 WordPress,版本被隐藏。 |
NOT_AFFECTED | 版本 ≥ 其分支的修复版本。 |
| 规则 | 严重性 | 触发条件 |
|---|
GHSA-7hp8-traversal-pagename | CRITICAL | pagename 中任意解码层级出现 .. 组件 |
GHSA-7hp8-traversal-param | HIGH | 其他参数中出现相同原语(主题和扩展也会调用 locate_template()) |
GHSA-7hp8-traversal-path | HIGH | URL 路径中出现 .. 组件(nginx 允许 %2f 通过,Apache 默认不允许) |
GHSA-7hp8-overlong-encoding | MEDIUM | UTF-8 超长编码(%c0%ae)。在 Linux 上无效,但绝无正当用途 |
GHSA-7hp8-theme-page-dir-probe | LOW | 探测主题的 page-* 目录 —— 侦察行为 |