# CVE-2026-87902: Critical Unauthenticated Local File Inclusion in WordPress Core (Conditional RCE)

> An unauthenticated attacker can abuse WordPress core's get_page_template() page-template resolution to include an arbitrary readable local .php file outside the active theme directory, via a double URL-encoded pagename query parameter that survives sanitization and is later decoded, letting traversal reach files such as PHP's pearcmd.php. On servers where the active theme has a top-level page-* directory and register_argc_argv is enabled (default in official Docker PHP images and common on cPanel/PHP<8.5), the LFI chains into full remote code execution. WordPress 7.1.2 (with backports to 7.0.6, 6.9.9, 6.8.10, 6.7.9, 6.6.9, and 4.7.37) fixes the flaw; multiple public PoCs exist and Patchstack observed the first exploitation probes roughly 5 hours after the patch shipped.

- **Published:** 2026-09-22T21:00:00Z
- **Last reviewed:** 2026-09-27T03:05:25.893Z
- **Canonical:** https://intel.threadlinqs.com/threat/TL-2026-2623
- **Card image:** https://intel.threadlinqs.com/og/threat/threat-intel-wordpress-path-traversal-rce-tl-2026-2623.webp?v=dbdf732d (Threadlinqs threat intel: CVE-2026-87902 WordPress LFI to RCE: Detection & IOCs (TL-2026-2623) — Critical severity, 48 IOCs, 9 detection rules.)
- **ID:** TL-2026-2623
- **Severity:** CRITICAL (CVSS 9.2)
- **Category:** VULNERABILITY
- **Status:** ACTIVE
- **Detections:** 9 (1 shown free below) · **IOCs:** 48 (3 shown free below)
- **CVEs:** CVE-2026-87902

## Description

CVE-2026-87902 is a critical (CVSS 9.2, CVSS4 vector AV:N/AC:L/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H; NVD also lists CVSS 3.1 8.1, AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H) path-traversal-to-local-file-inclusion vulnerability (CWE-98, Improper Control of Filename for Include/Require in PHP) in WordPress core versions 4.7.0 through 7.1.1, affecting the decade-plus of releases in that range. The root cause is in wp-includes/template.php inside get_page_template(): when WordPress resolves which template to render for a requested page, it builds a candidate filename by concatenating a caller-influenced value onto a fixed 'page-' prefix and '.php' suffix. A neighboring code branch three lines away already applies a validate_file() containment check before using similar input; the pagename branch does not, an inconsistency in security controls across otherwise-similar operations that Robert Ressl (the discoverer) documented in his write-up and PoC.

The practical exploitation path relies on double URL-encoding: WordPress's request sanitizer strips literal '../' sequences but does not recognize percent-encoded octets (e.g. %252e%252e, i.e. '..' encoded twice), so a payload like 'templates%252f%252e%252e%252f...' survives the initial filter. Later, when get_page_template() calls urldecode() on the pagename value, the encoded traversal sequence is converted into real '../' path segments, and locate_template() concatenates the now-decoded value onto the theme path without re-validating containment. template-loader.php then includes whatever file that traversal resolves to, provided it exists and is readable, without confirming the resolved path stays inside an allowed theme directory. WordPress 7.1.2 closes this two ways: it applies validate_file() to the pagename branch to match the sibling branch, and adds a new _wp_is_template_path_allowed() containment check requiring all resolved template paths to remain inside allowed theme directories -- a defense-in-depth response suggesting broader concern about the template-resolution surface generally.

By itself the flaw is 'only' unauthenticated arbitrary local .php file inclusion (an attacker chooses which readable .php file on the server executes, not what code it runs) -- serious LFI, but not automatically RCE. Four conditions must align for the documented RCE chain: (1) a published, anonymous-accessible Page selectable via page_id with no custom template already assigned, (2) the active theme containing a top-level directory beginning with 'page-' (common in legacy default themes such as Twenty Twelve and Twenty Fourteen, and in themes like Neve, Hestia, and Sydney), (3) a readable .php file reachable via traversal that has exploitable runtime behavior, and (4) that file's environment supporting arbitrary content generation. Ressl's laboratory chain used PHP's bundled PEAR command-line entry point, pearcmd.php, which reads command-line-style arguments out of $_SERVER['argv'] when the PHP ini directive register_argc_argv is enabled -- the default in the official PHP Docker images and common in cPanel hosting running PHP versions below 8.5. A first unauthenticated request abuses pearcmd.php's config-create functionality (invoked via a crafted 'pagename' that traversal-includes it, with query-string content acting as PEAR CLI arguments) to write a second, attacker-controlled PHP file to a writable temp directory; a second request re-exploits the same LFI to include that generated file, achieving code execution as the web server's account (www-data in testing). Ressl explicitly cautions that his evidence 'does not establish' that every default WordPress installation is exploitable to full RCE -- the chain is environment-dependent (PEAR availability, register_argc_argv, writable temp paths), not a WordPress-specific guarantee -- but the underlying unauthenticated LFI is present in every affected core version regardless of those conditions.

WordPress disclosed and patched the issue on 2026-09-22. Independent of the vendor advisory, Robert Ressl published a detailed technical write-up and a Docker-based reproduction lab (pinned to wordpress:7.0.2-php8.3-apache with register_argc_argv deliberately enabled) at ressl.ch, and at least two further public PoC repositories (GitHub user abraxas, and a listing by 'pwnVader' indexed on Sploitus) appeared the same day, each independently demonstrating the traversal-to-inclusion primitive with slightly different encoding depth and target-file choices. Patchstack's WAF telemetry recorded the first exploitation probes at 2026-09-22T17:44Z -- under five hours after the WordPress.org patch release -- from a small, concentrated cluster of source addresses (two neighboring IPv4 hosts plus supplementary IPv6 traffic) using automated Go-http-client requests with double-encoded traversal against wp-links-opml.php, wp-includes/functions.php, and wp-cron.php as detection targets; as of the most recent telemetry (2026-09-22T19:51Z) this activity remained reconnaissance/fingerprinting rather than confirmed payload delivery, and the CVE had not yet been added to the CISA KEV catalog. Given WordPress's ~43% share of the web and the fact that a large majority of live installs lag the latest release, and the unusually deep backport (patches were issued all the way back to the 4.7 branch, first released in 2016), the vendor's own patch scope signals broad recognized exposure across long-lived, infrequently-updated WordPress deployments.

## MITRE ATT&CK

- T1595.002 Vulnerability Scanning
- T1592.002 Software
- T1588.005 Exploits
- T1190 Exploit Public-Facing Application
- T1059 Command and Scripting Interpreter
- T1505.003 Web Shell
- T1027 Obfuscated Files or Information
- T1140 Deobfuscate/Decode Files or Information
- T1005 Data from Local System
- T1588.006 Obtain Capabilities
- T1059.004 Command and Scripting Interpreter
- T1083 File and Directory Discovery
- T1552.001 Unsecured Credentials
- T1105 Ingress Tool Transfer
- T1588.002 Obtain Capabilities: Tool
- T1608.001 Stage Capabilities: Upload Malware
- T1518 Software Discovery
- T1033 System Owner/User Discovery
- T1082 System Information Discovery

## Free detection (Splunk SPL, Microsoft KQL, Sigma)

**WordPress CVE-2026-87902 Double URL-Encoded Pagename Traversal Probe — Exploitation** (`TL-2026-2623-SPL-001`)

Detects the unauthenticated exploitation primitive for CVE-2026-87902: requests carrying a double URL-encoded traversal sequence (%252e%252e / %252f) in the pagename query parameter, or requests fingerprinting the vulnerable install by probing wp-links-opml.php, wp-includes/functions.php, or wp-cron.php — the exact target set Patchstack observed in the first in-the-wild probes ~5 hours after the 7.1.2 patch.

- **Languages:** Splunk SPL, Microsoft KQL, Sigma
- **ATT&CK:** T1190, T1595.002
- **Data sources:** web_server_access_logs, waf_logs, w3c_iis_log
- **Severity:** high
- **Confidence:** high

### Splunk SPL

```spl
index=web sourcetype=access_combined
| rex field=uri_query "pagename=(?<pagename_val>[^&]+)"
\`comment("CVE-2026-87902: WordPress literal-'../'  sanitizer misses percent-encoded octets; urldecode() inside get_page_template() later reveals the traversal.")\`
| where match(uri_query, "%252[eE]%252[eE]") OR match(uri_query, "%252[fF]") OR match(uri_path, "(?i)(wp-links-opml\.php|wp-cron\.php|wp-includes/functions\.php)")
| eval double_encoded_traversal=if(match(uri_query,"%252[eE]%252[eE]"),1,0)
| eval is_scan_ua=if(match(useragent,"(?i)Go-http-client"),1,0)
| stats count as request_count min(_time) as first_seen max(_time) as last_seen values(uri_path) as targeted_paths values(useragent) as user_agents values(pagename_val) as pagename_values values(double_encoded_traversal) as had_double_encoding by clientip
| where request_count >= 1
| sort -request_count
```

### Microsoft KQL

```kql
W3CIISLog
| where TimeGenerated > ago(24h)
// CVE-2026-87902: match either the raw double-encoded traversal payload or the Patchstack-documented fingerprinting target set
| where (csUriQuery has "pagename" and (csUriQuery has "%252e%252e" or csUriQuery has "%252f"))
   or csUriStem has_any ("wp-links-opml.php", "wp-cron.php", "wp-includes/functions.php")
| extend DoubleEncodedTraversal = iif(csUriQuery has "%252e%252e", true, false)
| summarize RequestCount=count(), FirstSeen=min(TimeGenerated), LastSeen=max(TimeGenerated), TargetPaths=make_set(csUriStem), UserAgents=make_set(csUserAgent) by cIP
| where RequestCount >= 1
| sort by RequestCount desc
```

### Sigma

```yaml
title: WordPress CVE-2026-87902 Double URL-Encoded Pagename Traversal Probe
id: 8f3a1c2e-4b5d-4e6f-9a1b-2c3d4e5f6a7b
status: experimental
description: Detects unauthenticated exploitation probes for CVE-2026-87902, an LFI in WordPress core's get_page_template() reached via a double URL-encoded pagename parameter, or fingerprinting requests against the specific files Patchstack observed scanners targeting.
references:
    - https://github.com/WordPress/wordpress-develop/security/advisories/GHSA-7hp8-65ch-5whp
    - https://patchstack.com/articles/cve-2026-87902-attackers-started-probing-wordpress-sites-hours-after-the-patch/
author: AII-Detector — ThreadLinqs Intelligence
date: 2026-09-22
tags:
    - attack.reconnaissance
    - attack.t1595.002
    - attack.initial-access
    - attack.t1190
    - cve.2026.87902
logsource:
    category: webserver
    product: nginx
detection:
    selection_traversal:
        cs-uri-query|contains:
            - '%252e%252e'
            - '%252E%252E'
    selection_targets:
        cs-uri-stem|contains:
            - 'wp-links-opml.php'
            - 'wp-cron.php'
            - 'wp-includes/functions.php'
    condition: selection_traversal or selection_targets
falsepositives:
    - Authorized vulnerability scanners or pentest tooling exercising the same endpoints
    - Misconfigured reverse proxies that legitimately double-encode query strings
level: high
```

**Known false positives:**

- Authorized vulnerability scanners (Nessus/Qualys) or pentest tooling probing the same endpoints
- Misconfigured reverse proxies or CDNs that legitimately double-encode query strings
- Internal security-team validation traffic re-testing the patch

**Tuning notes:** Baseline known scanner/proxy source IPs into an allowlist lookup to suppress benign double-encoding traffic; escalate severity when the same source hits multiple PoC-specific paths (wp-links-opml.php, wp-cron.php, wp-includes/functions.php) within a short window, matching the Patchstack cluster pattern.

### 8 more detection rules (Splunk SPL, Microsoft KQL, Sigma), unlock with TL Creds

- WordPress CVE-2026-87902 Double URL-Encoded Pagename Traversal Probe — Exploitation (locked: unlock with TL Creds)
- WordPress pearcmd.php Webshell Write & Web-Server-Spawned Shell — Post-Exploitation (locked: unlock with TL Creds)
- Post-RCE Web-Server-Process Public Egress & Repeated Sensitive-Path Recon (CVE-2026-87902) (locked: unlock with TL Creds)
- WordPress CVE-2026-87902 Double URL-Encoded Pagename Traversal Probe — Exploitation (locked: unlock with TL Creds)
- WordPress pearcmd.php Webshell Write & Web-Server-Spawned Shell — Post-Exploitation (locked: unlock with TL Creds)
- Post-RCE Web-Server-Process Public Egress & Repeated Sensitive-Path Recon (CVE-2026-87902) (locked: unlock with TL Creds)
- WordPress pearcmd.php Webshell Write & Web-Server-Spawned Shell — Post-Exploitation (locked: unlock with TL Creds)
- Post-RCE Web-Server-Process Public Egress & Repeated Sensitive-Path Recon (CVE-2026-87902) (locked: unlock with TL Creds)

Sign up free to unlock the detection rules: https://threadlinqs.com/?modal=signup

## Sample indicators (3 of 48)

Defanged, TLP:CLEAR.

- IP address: `169.58.48[.]193`
- URL: `hxxps://raw[.]githubusercontent[.]com/MrG3P5/web-shell/refs/heads/main/uploader.php`
- IP address: `169.58.48[.]195`

Full set of 48 indicators unlocks with TL Creds: https://threadlinqs.com/tl-creds

## Sources

- [WordPress Issues Patch for Critical Flaw That Can Enable Code Execution on Some Servers](https://thehackernews.com/2026/09/wordpress-issues-patch-for-critical.html)
- [Unauthenticated path traversal in page-template resolution leading to conditional RCE (GHSA-7hp8-65ch-5whp)](https://github.com/WordPress/wordpress-develop/security/advisories/GHSA-7hp8-65ch-5whp)
- [NVD - CVE-2026-87902 Detail](https://nvd.nist.gov/vuln/detail/CVE-2026-87902)
- [CVE-2026-87902 WordPress technical write-up and PoC lab](https://ressl.ch/blog/cve-2026-87902-wordpress/)
- [ressl/cve-2026-87902-poc: PoC for unauthenticated path traversal in WordPress page-template resolution](https://github.com/ressl/cve-2026-87902-poc)
- [abraxas/CVE-2026-87902: Unauthenticated Local File Inclusion (conditional RCE)](https://github.com/abraxas/CVE-2026-87902)
- [CVE-2026-87902: Attackers Started Probing WordPress Sites Hours After the Patch](https://patchstack.com/articles/cve-2026-87902-attackers-started-probing-wordpress-sites-hours-after-the-patch/)
- [WordPress 7.1.2 Security Release: Unauthenticated LFI to RCE](https://patchstack.com/articles/wordpress-7-1-2-security-release-unauthenticated-lfi-to-rce/)
- [CVE-2026-87902: WordPress RCE Flaw Already Exploited](https://byteiota.com/cve-2026-87902-wordpress-rce-patch-now/)
- [CVE-2026-87902: Critical WordPress RCE Flaw Fixed in Version 7.1.2](https://securityonline.info/wordpress-rce-vulnerability-cve-2026-87902/)
- [pwnVader exploit listing for CVE-2026-87902](https://sploitus.com/exploit?id=5F650CE9-222D-5AA8-A6CC-240BDBDE6253)

## Full data

The other 8 detection rules (Splunk SPL / Microsoft KQL / Sigma) unlock with TL Creds; a free account includes daily creds: https://threadlinqs.com/?modal=signup
Indicator values unlock with TL Creds, at a lower price on Red and Purple; AI agents can query rules and IOCs through the Threadlinqs MCP server (Purple): https://intel.threadlinqs.com/mcp

Canonical: https://intel.threadlinqs.com/threat/TL-2026-2623
