Critical flaws CVE-2026-87902 + xss2shellTest my site
WordPressLFIPath traversalRCEVulnerability

CVE-2026-87902: unauthenticated path traversal in WordPress core

CVE-2026-87902 affects WordPress 4.7.0 to 7.1.1: an unauthenticated path traversal lets an attacker include a local PHP file. Affected versions, the 7.1.2 fix, a detection test and remediation.

by Thibaud Robin12 min read
CVE-2026-87902: unauthenticated path traversal in WordPress core

Why CVE-2026-87902 demands immediate action

On September 22, 2026, the WordPress security team released version 7.1.2, along with backported fixes for every branch down to 4.7. A single flaw is patched, but it is a major one: CVE-2026-87902, an unauthenticated path traversal in page template resolution, rated 9.2 out of 10 on CVSS v4.0 (critical). Without any account, an attacker can make WordPress include a PHP file located elsewhere on the server, outside the active theme. That inclusion leads, under some conditions, to code execution.

This is not theoretical. According to Patchstack, probing attempts showed up in logs less than five hours after the patch shipped. As with wp2shell and xss2shell the month before, the gap between disclosure and the first exploitation is measured in hours. This article covers the mechanism of the flaw, offers a non-destructive detection test to check your exposure, and details the remediation.

Inside the bug: template resolution that leaves its folder

When WordPress renders a page, it picks the theme file that will display it. That selection goes through get_page_template(), a core function that builds a file path from the requested page, then includes it. The problem: the value that influences that path is not sufficiently constrained. An attacker can slip a directory traversal into it, a run of ../ that climbs the tree, and point the resolution at a .php file outside the theme.

WordPress then includes that file, and PHP executes the code it contains. That is the very definition of a local file inclusion. The flaw triggers without authentication: it needs no account and no action from a legitimate user.

The role of double encoding

The detail that makes the flaw interesting, and explains why it survived upstream sanitisation, is double encoding. The payload is written into the page parameter twice percent-encoded (for example %252f for a /). WordPress's sanitisation layer lets that value through, taking it for a harmless string, and the template resolution then decodes it one time too many and rebuilds the traversal. Two steps that looked safe in isolation contradict each other once chained.

Another useful detail for reading your logs: exploitation requests also carry a valid page id. Without a real page to attach to, the request falls back to a 404 and the vulnerable code never runs. The joint presence of a legitimate page_id and a page parameter stuffed with encoded sequences is therefore a clean signature.

From LFI to code execution: a conditional escalation

Let us be honest about the scope. The certain part is the inclusion: on an affected version, an attacker gets a local file of their choice included. The conditional part is turning that inclusion into a webshell. To execute arbitrary code, the attacker needs a .php file that is readable and holds content they control to already exist on the server, then have the flaw include it.

Depending on the environment, that file can come from several places: a log file into which controlled text was injected, a session file, an upload accepted elsewhere on the site. None of those conditions is universal, and a hardened server rarely gathers several of them. That explains the gap between the raw severity (an unauthenticated LFI in WordPress core, hence a critical score) and the real likelihood of a full RCE, which depends on the ground. We deliberately do not detail a ready-to-run chain: on a production target, reproducing it amounts to exploiting it for real.

Kill chain

From anonymous request to code execution

Phase 1Local file inclusion, without authentication
  1. 1

    Anonymous request

    a real page is requested with a booby-trapped template parameter.

  2. 2

    Double-encoded traversal

    the value survives sanitisation, then get_page_template() decodes it and leaves the theme folder.

  3. 3

    Inclusion outside the theme

    WordPress includes a chosen local PHP file and PHP runs its content.

Phase 2Escalation to a shell (conditional)
  1. 4

    A controlled PHP file already present

    a poisoned log, a session file or an upload accepted elsewhere on the site.

  2. Code execution

    including that file runs the attacker's code in the web server context.

The first phase, including a local file outside the theme, is certain on an affected version. The second, code execution, stays conditional: it assumes a PHP file with controlled content is already present and readable on the server.

Am I vulnerable? Affected versions and the fix

The official advisory is clear: the flaw affects WordPress 4.7.0 up to and including 7.1.1. The fix shipped in 7.1.2 and was backported to every still-maintained branch, down to 4.7.37, as a courtesy to sites that stayed on an older major version.

WordPress versionStatusFix
4.7.0 to 7.1.1In the affected range, status to confirm against your branch's latest maintenance release7.1.2, or the latest maintenance release on your channel (backported down to 4.7.37)
7.1.2 and laterFixed with certaintyAlready applied

The flaw was reported by researcher Robert Ressl, and tracked under advisory GHSA-7hp8-65ch-5whp. It is the fifth core security release since July 2026: the cadence alone says something about the scrutiny WordPress is under this year.

Proof of concept: test your exposure without breaking anything

Good news: CVE-2026-87902 can be tested without a destructive exploit. We do not try to read a secret or run code. We simply verify that template resolution leaves its folder, by having it include a WordPress core file that is entirely harmless: wp-links-opml.php, which only emits a small public OPML document (the site's link list). If its marker shows up in the response of a normal page, inclusion outside the theme is proven. This is exactly the logic of the public detection templates.

The manual test with curl

We request a real page (here page_id=2, the sample page of a default install) whose template parameter carries a double-encoded traversal toward wp-links-opml.php, then look for the OPML marker in the response.

curl -s "https://YOUR-SITE/?page_id=2&pagename=templates%2f..%252f..%252f..%252f..%252fwp-links-opml" \
  | grep -i '<opml\|generator="WordPress'

Reading the result:

  • The command prints an <opml ...> line or a generator="WordPress/...": the core file was included outside the theme, your site is vulnerable. Update without delay.
  • The command prints nothing: the marker did not come back. That is a good sign, but not proof of a fix on its own. The same silence can come from a WAF, an unusual theme, a missing page_id or a plain redirect. To conclude, cross-check with the version (7.1.2 or your branch's latest maintenance release).

The exact traversal depth (the number of ../) depends on the installed theme's directory layout. If the marker does not appear although the version is old, adjust that number, or let the scanner handle it.

Automated scanning with Nuclei

To check a whole estate rather than one URL, the community published Nuclei templates (see pull requests #17300 and #17302 in the nuclei-templates repo). They work behaviorally and non-invasively: they compare the size and content of the response between a normal page and the same page with the traversal, without reading any sensitive data.

nuclei -u https://YOUR-SITE -id CVE-2026-87902

A site flagged by the template is vulnerable. No result only means the inclusion was not observed, not that the site is fixed. Only the version (7.1.2 or higher) settles it with certainty.

What to do now: the remediation plan

In order of effectiveness:

  1. Update to WordPress 7.1.2, or to the latest fix on your branch if you stayed on an earlier major version. It is the only remediation that truly closes the flaw. The security update installs without a major version jump.
  2. Reduce the execution surface in the meantime: make sure PHP does not execute from your upload and log directories, and that filesystem permissions prevent writing where the server can include.
  3. Log and monitor suspicious requests (see below), to spot an attempt before it succeeds.

Spotting attempts in your logs

You do not need to send anything to spot an ongoing attack. The signature is telling enough:

  • a GET / request carrying both a valid page_id= and a pagename (or template) parameter containing double-encoded traversal sequences: %252f, ..%252f, possibly %252e%252e;
  • depth variants of the same pattern, fired in bursts against the same host, the sign of a scan hunting for the right traversal length.

For indicators of compromise, watch for unknown PHP files appearing in upload directories, and unusual access to log or session files right after those requests.

Three WordPress flaws in two months: the lesson

wp2shell came through the REST API, xss2shell through the login screen, CVE-2026-87902 through template resolution. Three different vectors, one takeaway: critical WordPress core flaws keep coming, they hit default installs, and they get exploited within hours of going public.

The annual pentest sees none of the three: a flaw disclosed on September 22 does not exist in a report dated last spring. The version scanner catches the easy part, the number in range, but gets fooled the moment the version is hidden and misses the backport context. And the incomplete perimeter stays everyone's weak spot: it is the old WordPress forgotten on a subdomain that falls first. To find it, you first need to map your whole information system and inventory your external attack surface.

Detecting CVE-2026-87902 continuously with Flawfence

At Flawfence, the logic does not change from one flaw to the next. The engine maps the perimeter, identifies every exposed WordPress instance including forgotten shadow IT, places its version within the known vulnerable ranges and checks exposure to the relevant vectors, without ever sending a destructive payload.

Facing CVE-2026-87902, concretely, Flawfence:

  • discovers your exposed WordPress sites, across all your domains and subdomains;
  • places the version below 7.1.2 and flags old branches to cross-check against their latest backported fix;
  • probes template resolution with a benign inclusion marker, which confirms the traversal when an affected version lets the core file come back;
  • monitors continuously, so a new vulnerable subdomain or a version rollback triggers an alert, without waiting for the next audit.

Frequently asked questions about CVE-2026-87902

What is CVE-2026-87902?

It is a critical WordPress core vulnerability, an unauthenticated path traversal in page template resolution (get_page_template()). It lets an attacker include a chosen local PHP file outside the active theme, which is a local file inclusion (LFI) and can lead to code execution depending on the configuration. It is rated 9.2 out of 10 on CVSS v4.0.

Which WordPress versions are affected?

Every version from 4.7.0 to 7.1.1 inclusive. The fix arrived with 7.1.2 on September 22, 2026, and was backported to every maintained branch down to 4.7.37. Any site below 7.1.2 should be treated as affected until it has taken the latest fix on its channel.

Is the flaw actively exploited?

Yes, probing was observed very quickly. According to Patchstack, the first attempts showed up in logs less than five hours after the patch shipped. A successful exploitation, however, depends on server conditions that are not always met, which is no reason to wait: the probing itself is massive and immediate.

Is an LFI really as serious as an RCE?

The inclusion itself is certain and unauthenticated, hence the critical score. The step to code execution is conditional: it needs a PHP file with controlled content already readable on the server. On many installs, that condition ends up being met, directly or by combining the flaw with another. So we treat CVE-2026-87902 as a top-tier threat, without over-dramatising the odds of an immediate RCE on a well-hardened server.

How do I test without risking my site?

With the test in the Proof of concept section: we have a public core file (wp-links-opml.php) included and check whether its marker comes back. No secret is read, nothing is written or executed. Test it with curl or the Nuclei template, only on your own assets, or let the Flawfence scanner do it on a self-declared ownership basis.

Is blocking the flaw with a WAF enough?

It is a useful mitigation that reduces the noise, but it stays bypassable (the traversal encoding can vary) and does not fix the cause. The only complete remediation is the update to 7.1.2 or the latest fix on your branch.

In summary

CVE-2026-87902 restates a simple rule: a function that builds a file path from user input must constrain that input, or it ends up leaving its folder. Here, page template resolution let a double-encoded traversal through, and a default WordPress site became a file inclusion door.

The good news is the same as for the previous flaws: detecting this class of problem needs neither a destructive exploit nor a pentest. It needs an exhaustive inventory of your perimeter and continuous monitoring that tests the real behaviour of your assets. That is exactly what Flawfence does, for CVE-2026-87902 as for the next flaw that has no number yet.

Check your exposure now, it will only take 5 minutes.

Let’s discuss your external exposure

Request a personalized Flawfence demo and discover your real exposure level.