CVE Watch

WordPress Core CVE-2026-87902: Path Traversal Under Active Attack

· 6 min read
A glowing blue file cabinet of folders with an amber path escaping over its top edge and down to a file outside it

Make sure your WordPress is up to date. CVE-2026-87902 is a critical flaw in WordPress core itself, not a plugin, and it affects every release from 4.7 through 7.1.1. An attacker with no account can make WordPress load and run a PHP file it was never meant to touch. Attacks started within hours of the fix on September 22, 2026, and CISA has added it to its list of known exploited vulnerabilities.

At a glance

CVECVE-2026-87902 (advisory GHSA-7hp8-65ch-5whp)
SoftwareWordPress core: every site running an affected version
TypeUnauthenticated path traversal leading to local PHP file inclusion; remote code execution under certain conditions
SeverityCVSS 9.2 (critical, CVSS v4)
Affected4.7.0 through 7.1.1
Fixed7.1.2 (September 22, 2026), with backports to every branch down to 4.7.37, including 7.0.6, 6.9.9 and 6.8.10. Latest is 7.1.3 (October 6, 2026)
StatusActively exploited since September 22, 2026; in CISA’s Known Exploited Vulnerabilities catalog since September 25
CreditRobert Ressl

What’s wrong

The bug is in how WordPress picks the template file for a page.

When someone visits a page, WordPress builds a list of template file names to look for in the theme, such as page-about.php for a page with the slug about. The function that does this, get_page_template(), takes the page name from the request, URL-decodes it and drops it straight into a file name. Nothing checked that the decoded name stayed inside the theme folder.

So a request with an encoded ../ in the page name produces a “template” name that climbs out of the theme directory. The next step, locate_template(), glued that name onto the theme path, checked that the file existed, and handed it to the template loader. The loader runs it as PHP.

A few things have to line up:

  1. A real page. The request pairs the crafted page name with a valid page ID so it reaches the page-template code at all. Nearly every site has published pages.
  2. A matching theme folder. Because the template name starts with page-, the traversal works through a theme that has a top-level folder named page-templates, a common convention in classic themes.
  3. A PHP file worth running. On its own, the bug runs PHP files already on the server, outside the theme.

Turning that into full remote code execution takes one more piece: a server with PEAR’s pearcmd.php installed and PHP’s register_argc_argv setting on, which lets the query string reach that script as command-line arguments. With both, attackers use pearcmd.php to write a PHP file of their choosing to disk, then include it. That combination is common on stock PHP Docker images, which ship with PEAR and run without a php.ini, where register_argc_argv defaults to on.

What the attacks look like

Patchstack saw the first attempt at 11:49 UTC on September 22, the day of the patch, and the first file-write attempt at 15:34 UTC. By the next day public scanning tools were circulating, including a Nuclei template, and traffic had grown more than tenfold.

The attacks come in three stages:

  1. Inclusion check. A request pairs pagename with a valid page_id, using an encoded traversal sequence after a templates%2f prefix. It points at a harmless core file such as wp-links-opml.php or wp-includes/feed-rss2.php. If a normal page URL comes back as OPML or RSS, the inclusion worked.
  2. PEAR check. The inclusion points at pearcmd.php, trying /usr/local/lib/php/, /usr/share/php/ and /usr/share/pear/, with +config-show appended to see whether PEAR responds.
  3. File write. The argument changes to +config-create, which writes attacker-chosen content to a file, usually in /tmp or /var/tmp.

Files seen dropped include wp-pear-rce-flag.php, poc87902.php, luci_*.php and zeta_*.php. POST requests now outnumber GET.

The heaviest early sources, per Patchstack:

43.250.53.42
180.251.159.243
195.178.110.247
107.189.14.87
45.61.184.170
92.246.130.76

Traffic now comes from a few hundred addresses, so blocking these won’t protect you. Use them to search your logs. The same goes for the scanner user agents cve-2026-87902-poc/1.0 and nuclei-cve-2026-87902/1.0: most attacks fake an ordinary browser.

What to do

1. Update

Go to Dashboard → Updates and install the latest release, 7.1.3 as of this writing. It includes this fix plus seven more security fixes. If you’re on an older branch, install the latest release for that branch; every branch back to 4.7 got the fix. WordPress installs minor security releases automatically by default, so many sites patched themselves, but check. Hosts and developers sometimes switch automatic updates off.

2. Check whether you were hit

If your site ran an affected version after September 22:

  • Search your access logs for %2e%2e or %252e%252e in the pagename parameter, pagename values starting with templates%2f, pagename and page_id in the same request, and any pearcmd or +config- strings.
  • Look for PHP files in /tmp and /var/tmp, especially the names above. There’s no legitimate reason for one to be there. If you find one, treat the server as compromised.
  • Look for OPML or RSS output returned from ordinary page URLs in your logs. That means an inclusion succeeded, even if nothing was written.
  • Check your theme for a top-level page-templates folder. If it has one and the server has PEAR, assume the worst until you’ve checked.

If anything turns up, restore from a clean backup taken before September 22, rotate every password and key, replace the salts in wp-config.php, and review the server, not just WordPress, for anything else left behind.

3. Remove the second half of the chain

The file inclusion is WordPress’s bug. Turning it into code execution needed help from the server. Two changes close that door for this attack and the next one like it:

  • Turn off register_argc_argv in php.ini. A web server has no use for it. It doesn’t fix the inclusion, but it breaks the PEAR technique.
  • Remove PEAR from production servers if nothing uses it. It’s a package manager. It has no business being reachable from a website.
register_argc_argv = Off

If you can’t update right away, a firewall rule that blocks encoded traversal sequences in the pagename parameter stops the known attacks in the meantime.

The bigger lesson

This one is in core, so a short plugin list doesn’t protect you. What protects you here is updating fast. Attackers had working probes within hours of the release, because a security patch tells them exactly where to look. Automatic minor updates exist for exactly this reason. If you’ve turned them off, have something just as fast in their place.

The other lesson is about the server. The WordPress bug was a file inclusion. Remote code execution came from a server that had a package manager installed and a PHP setting switched on that no website needs. Production servers should run only what the site needs, the same rule as for plugins. Every extra tool on the box is something an attacker can chain onto the next bug.

For the record, wpbob.com was already on 7.1.3, and its theme has no page-templates folder.

Sources