---
schema: 1
kind: vulnerability
title: "CVE-2026-87902 — WordPress Core: unauthenticated page-template path traversal to conditional remote code execution, weaponised within a day (CVSS4.0 9.2)"
headline: "WordPress's fix for a pre-auth file-inclusion bug is already outrun by a public Nuclei template and confirmed file-write attempts"
summary: >
  WordPress Core versions 4.7.0 through 7.1.1 carry an unauthenticated path-traversal
  flaw in page-template resolution that lets a remote attacker include a chosen
  readable local PHP file outside the active theme, reaching code execution when the
  active theme has a top-level directory named with a "page-" prefix and the server
  exposes PHP's PEAR pearcmd.php entry point. WordPress shipped the fix on
  2026-09-22 (7.1.2, backported to every branch back to 4.7.37); within 24 hours
  Patchstack recorded confirmed file-write exploitation attempts and a named public
  Nuclei template, with traffic running at more than ten times the first evening's
  volume.
discovered_at: "2026-09-24T04:30:00Z"
updated_at: null
event_date: "2026-09-22"
run_id: 2026-09-24T0405Z-intel
priority: critical
immediate_action:
  title: "Patch every WordPress install to its branch's fixed release now — exploitation is already writing files to disk"
  action: >
    WordPress fixed CVE-2026-87902 on 2026-09-22 (7.1.2, with matching security
    backports on every branch back to 4.7.37), and within 24 hours Patchstack
    recorded confirmed pearcmd-based file-write exploitation attempts plus a
    named public Nuclei template now driving traffic at more than ten times the
    first evening's volume. Update to the branch-appropriate fixed release
    immediately. Where an update cannot land same-day, block traversal
    sequences in the `pagename` request parameter at the WAF/reverse-proxy
    layer (a real page slug never contains one) and disable
    `register_argc_argv` for web-facing PHP to break the pearcmd escalation
    path without yet fixing the underlying inclusion.
tags: [vulnerabilities, rce, pre-auth, actively-exploited, poc-public, path-traversal]
regions: [global]
sectors: [public-sector]
entities: ["product:wordpress-core"]
techniques: [T1190]
affected_products: ["WordPress Core"]
cves:
  - id: CVE-2026-87902
    cvss: "9.2 (CVSS4.0)"
    epss: null
    type: path-traversal
    vector: zero-click
    auth: pre-auth
    status: [exploited, poc-public, patch-available]
    affected: "4.7.0 – 7.1.1 (every branch from 4.7 onward)"
    fixed: "7.1.2 (7.1.x), with matching security backports on every other branch down to 4.7.37 — see the advisory's full per-branch table"
sources:
  - url: "https://github.com/WordPress/wordpress-develop/security/advisories/GHSA-7hp8-65ch-5whp"
    publisher: "WordPress Security Team (GHSA-7hp8-65ch-5whp)"
    date: "2026-09-22"
    role: primary
  - url: "https://ressl.ch/blog/cve-2026-87902-wordpress/"
    publisher: "Robert Ressl (discovering researcher)"
    date: "2026-09-22"
    role: primary
  - url: "https://patchstack.com/articles/cve-2026-87902-attackers-started-probing-wordpress-sites-hours-after-the-patch/"
    publisher: "Patchstack"
    date: "2026-09-23"
    role: primary
  - url: "https://www.wordfence.com/blog/2026/09/psa-critical-unauthenticated-path-traversal-vulnerability-patched-in-wordpress-core/"
    publisher: "Wordfence Threat Intelligence"
    date: "2026-09-22"
    role: corroborating
closed_sources: []
evidence:
  - quote: "An unauthenticated attacker can make `get_page_template()` page-template resolution include a chosen readable local `.php` file outside the active theme directories. If relevant pre-conditions for both the server environment and the active theme are met, this can lead to RCE."
    publisher: "WordPress Security Team (GHSA-7hp8-65ch-5whp)"
  - quote: "Attackers are now including `pearcmd.php` and using it to write PHP files to disk, and public scanning tooling for this CVE is in circulation."
    publisher: "Patchstack"
  - quote: "A named Nuclei template means this is no longer a handful of operators working from the patch diff. It is in general circulation and anyone can point it at a host list."
    publisher: "Patchstack"
  - quote: "The important distinction is between canonicalization and containment. Resolving a path with `realpath()` gives a normalized destination. It does not establish that the destination belongs to a directory the application intended to trust."
    publisher: "Robert Ressl"
verification: multi-source
sourcing_note: "CVSS4.0 9.2 is the score in WordPress's own GHSA advisory; NVD's independent secondary CVSS3.1 assessment scores it 8.1 (High) — both are reported, the vendor's own CVSS4.0 figure leads the title per the per-CVE-authority rule. NVD's automated SSVC exploitation flag ('none', timestamped 2026-09-22T16:56Z) predates Patchstack's 2026-09-23 confirmation of active file-write exploitation and is superseded by it."
confidence: high
references: []
deep_dive: true
deep_dive_category: web-app-rce
org_triage: null
classification:
  reliability: A
  credibility: 1
watchlist_hit: false
actions:
  - "Patch every WordPress install (any version from 4.7.0 through 7.1.1) to its branch's fixed release now; where same-day patching is not possible, block `pagename` values containing percent-encoded traversal sequences at the WAF/reverse-proxy layer and disable `register_argc_argv` for web-facing PHP."
  - "Check `/tmp` and `/var/tmp` on every WordPress host that was internet-reachable and unpatched since 2026-09-22 for unexpected `.php` files — their presence means a file-write attempt already succeeded and the host must be treated as compromised, not merely probed."
updates: []
migrated_from: null
---

CVE-2026-87902 is an unauthenticated path-traversal flaw in WordPress Core's page-template resolution that lets a remote attacker force `get_page_template()` to include a chosen, readable local PHP file from outside the active theme's directories ([WordPress Security Team, 2026-09-22](https://github.com/WordPress/wordpress-develop/security/advisories/GHSA-7hp8-65ch-5whp)). The request pairs two public query variables, `pagename` and `page_id`, which WordPress accepts from an anonymous form POST ([Robert Ressl, 2026-09-22](https://ressl.ch/blog/cve-2026-87902-wordpress/)) — Patchstack's observed traffic shows the same fields also work over a GET request, with POST later overtaking GET as the more common method ([Patchstack, 2026-09-23](https://patchstack.com/articles/cve-2026-87902-attackers-started-probing-wordpress-sites-hours-after-the-patch/)) — with no account, cookie, session or nonce required ([Robert Ressl, 2026-09-22](https://ressl.ch/blog/cve-2026-87902-wordpress/)). A valid `page_id` is what makes the request resolve to a real page at all: without one the query 404s and the vulnerable template-resolution code never runs, so the pairing is not padding but a load-bearing part of the chain ([Patchstack, 2026-09-23](https://patchstack.com/articles/cve-2026-87902-attackers-started-probing-wordpress-sites-hours-after-the-patch/)). WordPress's own slug sanitiser preserves percent-encoded octets while rewriting literal dots and truncating at literal slashes ([Patchstack, 2026-09-23](https://patchstack.com/articles/cve-2026-87902-attackers-started-probing-wordpress-sites-hours-after-the-patch/)), so a double-encoded traversal sequence reaches query processing with its percent-encoded octets still present, and the encoded separators are not treated as ordinary path separators at that point ([Robert Ressl, 2026-09-22](https://ressl.ch/blog/cve-2026-87902-wordpress/)); `get_page_template()` then calls `urldecode()` on the surviving value, turning the encoded characters into a live `../` sequence at the moment the template candidate is built, and the loader that resolves the final path checks only that the target exists, is readable and carries the expected suffix — never that the resolved path stays inside an allowed theme directory ([Robert Ressl, 2026-09-22](https://ressl.ch/blog/cve-2026-87902-wordpress/)). "The important distinction is between canonicalization and containment. Resolving a path with `realpath()` gives a normalized destination. It does not establish that the destination belongs to a directory the application intended to trust" ([Robert Ressl, 2026-09-22](https://ressl.ch/blog/cve-2026-87902-wordpress/)).

Reaching code execution, not just file inclusion, needs two further conditions on top of the traversal itself: the active theme (parent or child) must contain a top-level directory whose name starts with `page-` — the advisory names the legacy Twenty Twelve and Twenty Fourteen themes and third-party themes Neve, Hestia and Sydney as examples — and a `.php` target file must exist on the server and be readable by the web server account ([WordPress Security Team, 2026-09-22](https://github.com/WordPress/wordpress-develop/security/advisories/GHSA-7hp8-65ch-5whp)). The demonstrated route uses PHP's PEAR `pearcmd.php` entry point, present by default in the official PHP Docker image and in cPanel installs on PHP versions before 8.5 ([WordPress Security Team, 2026-09-22](https://github.com/WordPress/wordpress-develop/security/advisories/GHSA-7hp8-65ch-5whp)) — the discoverer's own lab used the `wordpress:php8.3-apache` tag specifically ([Robert Ressl, 2026-09-22](https://ressl.ch/blog/cve-2026-87902-wordpress/)); with `register_argc_argv` enabled, the query string reaches the included script as `$argv`, letting an attacker issue pearcmd's `config-create` action to write an attacker-chosen PHP file to `/tmp` or `/var/tmp` ([Patchstack, 2026-09-23](https://patchstack.com/articles/cve-2026-87902-attackers-started-probing-wordpress-sites-hours-after-the-patch/)) — code execution with the privileges of the PHP or web-server account ([Robert Ressl, 2026-09-22](https://ressl.ch/blog/cve-2026-87902-wordpress/)). WordPress shipped 7.1.2 for the 7.1 branch on 2026-09-22 and backported the fix to every other branch back to 4.7.37, so no affected site needs a forced major-version jump to patch ([WordPress Security Team, 2026-09-22](https://github.com/WordPress/wordpress-develop/security/advisories/GHSA-7hp8-65ch-5whp)).

Exploitation moved fast. The first requests hit Patchstack's sensors at 11:49 UTC on 2026-09-22, the same day the patch shipped, using the exact encoding the fix addresses — evidence the payloads were built from the patch diff, not an independent rediscovery ([Patchstack, 2026-09-23](https://patchstack.com/articles/cve-2026-87902-attackers-started-probing-wordpress-sites-hours-after-the-patch/)). What began as reconnaissance-only probing (pointing the inclusion at a harmless core file such as `wp-links-opml.php` to see if the host answers) escalated within a day into confirmed exploitation: "Attackers are now including `pearcmd.php` and using it to write PHP files to disk, and public scanning tooling for this CVE is in circulation" ([Patchstack, 2026-09-23](https://patchstack.com/articles/cve-2026-87902-attackers-started-probing-wordpress-sites-hours-after-the-patch/)). Some observed payloads write a harmless marker string consistent with building a vulnerable-host list; others write a short tag that executes a shell command on access. Traffic is now spread across a few hundred source addresses and running at more than ten times the first evening's volume, and it has been commoditised: "A named Nuclei template means this is no longer a handful of operators working from the patch diff. It is in general circulation and anyone can point it at a host list" ([Patchstack, 2026-09-23](https://patchstack.com/articles/cve-2026-87902-attackers-started-probing-wordpress-sites-hours-after-the-patch/)).

**Detection:** the `pagename` and `page_id` parameters rarely co-occur in ordinary traffic, so their pairing at the site root or `/index.php`, combined with a `pagename` value carrying `%2e%2e` or double-encoded `%252e%252e`, is a low-noise signal to search web-access logs for. A `pagename` beginning with `templates%2f` or another `page-` prefixed directory name is a further tell, since the traversal has to continue a real directory before it can climb out of the theme. Any request containing `pearcmd`, `+config-show` or `+config-create` marks the escalation stages specifically. Host-side, an OPML or RSS-feed response returned from what should be a normal page URL confirms a stage-one probe worked even without further action, and any unexpected `.php` file in `/tmp` or `/var/tmp` means a file-write attempt already succeeded — treat that host as compromised, not merely scanned. **Triage:** a genuine WordPress page request never carries a `pagename` value with traversal syntax, and `pagename` plus `page_id` arriving together is itself anomalous, so both signals should carry a very low false-positive rate.

**Defender takeaway:** patch every WordPress instance to its branch-appropriate fixed release now; where that cannot happen the same day, reject traversal sequences in `pagename` at the WAF layer and disable `register_argc_argv` for web-facing PHP, which breaks the pearcmd escalation path without yet fixing the underlying inclusion bug.
