flawopen.com/Teardowns/cve-2026-87902-wordpress-page-template-path-traversal
CVE-2026-87902: WordPress Page-Template Path Traversal
An unvalidated decoded page name entered WordPress template resolution, allowing unauthenticated local PHP file inclusion and conditional RCE when the theme and PHP environment met specific prerequisites.[1]
Think of a librarian who checks a book request against the shelves before fetching it. WordPress checked one route into its template list, but forgot to check the decoded page-name route; special path markers could make the librarian leave the theme shelves and fetch a PHP file elsewhere. The fix checks the decoded name and verifies the resolved template remains in an allowed theme directory.[1], [2], [3]
Core Concepts & Subsystem Terms
Page-template resolution- WordPress builds an ordered list of candidate PHP templates for a page and asks the active theme to provide the first matching file.[1], [2]
Path traversal- Path components such as
..can escape an intended directory when input is incorporated into a filename without validation.[1] Local PHP file inclusion- The application loads a PHP file already present on the server. The advisory says this can become RCE only when additional theme and server conditions are met.[1]
`validate_file()`- A WordPress helper that rejects disallowed path components such as directory traversal before a candidate is used in a file path.[3]
Framework mappings
- CWE reference for the weakness described in this guide: CWE-98
- CISA KEV — added 2026-09-25 — CVE-2026-87902
Root Cause Analysis
In get_page_template() in wp-includes/template.php, WordPress URL-decodes the pagename query value and adds a decoded page-{name}.php candidate when it differs from the original. In vulnerable releases that decoded candidate was not passed to validate_file(), even though the adjacent custom-template branch was. The template loader could therefore resolve a candidate beyond the active theme directory. The official advisory limits the impact: an active parent or child theme needs a top-level directory beginning with page-, and a readable local PHP target must exist; RCE requires further PHP/server conditions. WordPress 7.1.2 validates the decoded candidate and adds a resolved-path containment check in locate_template(). The WordPress advisory classifies this weakness as CWE-98 (improper control of a filename used in a PHP include/require) and credits Robert Ressl with discovery and responsible disclosure.[1], [2], [3], [7]
CVE-2026-87902 reachability: exposure versus conditional RCE
| Deployment condition | What the primary sources establish | Operator action |
|---|---|---|
| Affected WordPress core branch | The GHSA lists affected versions by branch, from 4.7.0–4.7.36 through 7.1.0–7.1.1. The corresponding fixes range from 4.7.37 (oldest backport) to 7.1.2 (current branch), with a separate fixed release for each listed branch. | Check the installed core version against the matching branch fix. Upgrade versions older than 4.7 to a supported release. |
| Active theme layout | The active parent or child theme must contain a top-level directory whose name begins with page- (for example, page-templates). | Inspect both active theme directories. Recheck this prerequisite when the active theme changes. |
| Readable local PHP target | A chosen .php file must exist outside the active theme directories and be readable by the web-server account. | Review which local PHP files are readable by the web server; do not infer file inclusion from the CVE version range alone. |
| Conditional RCE environment | The advisory describes RCE only with additional PHP/server conditions. Its examples include pearcmd.php when register_argc_argv=On, the official PHP Docker image, and default cPanel configurations using PHP before 8.5. | Assess the actual PHP runtime and configuration. A vulnerable WordPress version alone does not establish RCE or compromise. |
Step-by-Step Attack Flow
Untrusted page name reaches template selection
An unauthenticated request supplies a page name that WordPress uses while building page-template candidates. No account or user click is required.[1]
Theme lookup can leave its intended root
If the active theme has the documented page-* directory and a readable local PHP target is present, template resolution may include a file outside the theme directory.[1]
RCE depends on additional environment conditions
The WordPress advisory describes remote code execution as conditional on further PHP/server prerequisites. Do not treat every affected WordPress installation as remotely exploitable or infer compromise from version alone.[1]
Source Code: Flaw vs. Secure Implementation
This excerpt compares the official WordPress 7.1.1 and 7.1.2 source tags. The patch applies the existing WordPress path validator after decoding and before adding the candidate. The same security release also adds _wp_is_template_path_allowed() in locate_template() to constrain resolved templates to approved roots. This is the upstream source change, not a complete standalone patch or an exploit demonstration.[2], [3]
Source excerpt: wp-includes/template.php — get_page_template()
if ( $pagename ) {
$pagename_decoded = urldecode( $pagename );
if ( $pagename_decoded !== $pagename ) {
$templates[] = "page-{$pagename_decoded}.php";
}
}
if ( $pagename ) {
$pagename_decoded = urldecode( $pagename );
if ( $pagename_decoded !== $pagename && 0 === validate_file( $pagename_decoded ) ) {
$templates[] = "page-{$pagename_decoded}.php";
}
}
Engineering & System Hardening Checklist
- ✓Upgrade to the fixed release for the installed branch; verify the running code version rather than relying only on an enabled auto-update setting.[1], [4]
- ✓Inventory internet-facing WordPress installations and identify unsupported versions older than 4.7, which do not receive this backport.[1], [4]
- ✓If an affected site was exposed before patching, review web and PHP logs for suspicious page-template requests and inspect for unexpected PHP files or accounts; treat a signal as an investigation lead, not proof by itself.
- ✓In custom template resolvers, validate decoded path components and enforce canonical real-path containment inside an explicit allowlist before loading files.[1], [3]
- ✓Keep WordPress Core and themes on supported releases; a theme-specific prerequisite can change when the active theme changes.[1], [4]