Yaamlabs
Vulnerabilities

WordPress core flaw exploited within hours of the fix

CVE-2026-87902, CVSS 9.2, lets an unauthenticated request hijack WordPress's page templates. Exploitation began the same day as the 7.1.2 patch.

By Yaali. September 26, 2026, 6 min read, Vulnerabilities, Patching.

Cover illustration of a website window with a file folder spilling loose document pages beneath it in a dark server room, with the Yaamlabs logo and the text: WordPress core flaw exploited within hours, 9.2 CVSS, fixed in WordPress 7.1.2

CVE-2026-87902 is a CVSS 9.2 flaw in WordPress core itself, not a plugin or theme, that lets an unauthenticated request make the page-template loader include a readable PHP file from outside the active theme. It reaches every release from 4.7.0 through 7.1.1, close to a decade of versions. WordPress fixed it in 7.1.2 on September 22, and because of the severity backported the same fix down through every branch it still supports, all the way to 4.7.37.

The story here is speed. Patchstack recorded the first exploitation attempt at 11:49 UTC on September 22, the same day 7.1.2 shipped, meaning attackers reverse-engineered the patch and were probing live sites within hours of its release. This sits in core rather than a plugin, so it will not show up in the plugin-vulnerability feeds most WordPress admins watch. The version to check is the one at the bottom of the Dashboard, under Updates, not the Plugins page.

Two-phase attack chain for CVE-2026-87902: an attacker request walks WordPress's page-template resolver outside the theme folder and includes pearcmd.php, which then writes a PHP payload to /tmp and runs it as the web server user

How it works

WordPress renders a "Page" post type by picking a template file from the active theme, using get_page_template() in wp-includes/template.php. That function builds a list of candidate filenames such as page-{slug}.php or page-{ID}.php from the page's pagename value, then hands them to locate_template(), which is meant to only look inside the active theme's own directory (and its parent theme's, for a child theme).

The pagename value comes from the request and gets run through urldecode() before it reaches that filename list. The decode happens once, but a double-encoded traversal sequence survives the first pass and gets decoded a second time further along in template resolution, turning back into a working ../. WordPress does call validate_file() elsewhere in its template code to block exactly this kind of traversal, but that check was missing on the pagename branch specifically. The fix in 7.1.2 adds the missing validate_file() call and a new _wp_is_template_path_allowed() function that checks every resolved template path, not just this one.

Reaching a file outside the theme needs one more thing to line up: the active theme has to contain a top-level directory whose name starts with page-, a naming pattern some theme template variants use. With that in place, and a request that points at a valid page_id, the loader can be walked out to any PHP file on disk that the web server account can read. That is local file inclusion (LFI): WordPress executes whatever code is in that file, exactly as if it were a template.

Local file inclusion is not automatically code execution of the attacker's choosing. It only runs code that is already on the server and does something useful with the attacker's input. That is what makes pearcmd.php the gadget of choice here. It is the command-line front end for PEAR, PHP's long-standing package manager, and it ships inside official PHP Docker images and on the default install of several Linux distributions and hosting stacks, whether or not anyone actually uses PEAR. When PHP has the register_argc_argv setting turned on, a request's own query string gets treated as if it were command-line arguments, which is exactly how pearcmd.php expects to receive its commands. That lets a web request invoke pearcmd's config-create command, which writes attacker-supplied content to an attacker-chosen path, such as a .php file in /tmp. Security researchers have documented pearcmd.php as a reliable LFI-to-RCE gadget for years, precisely because it is common, rarely removed, and does something an attacker can weaponize the moment it gets included.

What attackers are doing

Patchstack's telemetry shows a clean progression. The first requests, at 11:49 UTC on September 22, probed core files such as wp-links-opml.php and wp-includes/feed-rss2.php to fingerprint which sites were still vulnerable, before anyone touched the real bug. Attackers then moved to the pagename parameter itself, walking the traversal through common install paths for pearcmd.php, including /usr/local/lib/php/pearcmd.php, /usr/share/php/pearcmd.php and /usr/share/pear/pearcmd.php. Once a path answered, the same request pattern appended pearcmd's config-create command to drop a PHP file into /tmp or /var/tmp, with observed filenames including wp-pear-rce-flag.php and poc87902.php alongside randomised names.

Researchers reported a follow-up step that fetches a PHP upload or webshell script hosted on GitHub, giving the attacker a working file manager on the box rather than a one-shot payload. By September 23, Patchstack said attack volume against the flaw had grown more than tenfold from the day before, matching the point where public proof-of-concept code and a scanning template started circulating. CISA added CVE-2026-87902 to its Known Exploited Vulnerabilities catalog on September 25, giving US federal civilian agencies until September 28 to patch.

Timeline for CVE-2026-87902 from the September 22 patch and first exploit attempt at 11:49 UTC through the September 23 traffic surge to the September 25 CISA KEV listing

What to do

Update to WordPress 7.1.2 or later. Check Dashboard > Updates for the version number now; do not assume core patches itself the way a plugin update notice would remind you. If your site accepts WordPress's automatic background updates (the default since WordPress 3.7), it may have already applied 7.1.2 on its own, so confirm the version rather than assuming either way. If you are running an older major version for compatibility reasons, WordPress backported the fix to 7.0.6, 6.9.9, 6.8.10 and every other branch it still supports, down to 4.7.37, so there is a patched release on your branch.

If you cannot update today, cut the RCE path even though the LFI bug itself stays open. Find and remove or rename pearcmd.php if your server does not actually use PEAR: find / -name pearcmd.php 2>/dev/null locates it, and deleting it or making it unreadable to the web server user breaks this specific chain. Set register_argc_argv = Off in php.ini, which stops query-string values from reaching an included script as command-line arguments. Confirm allow_url_include is off too. None of these close the underlying file-inclusion bug, so update as soon as you can.

Check whether you were already hit. Look in /tmp and /var/tmp for unexpected .php files, particularly wp-pear-rce-flag.php, poc87902.php, or PHP files with names you do not recognize. Search your webroot for .php files modified since September 22 that are not part of your deployment, including inside wp-content/uploads, where no legitimate PHP file should ever sit. In your web server access log, grep for pearcmd and for pagename or page_id parameters containing encoded traversal sequences like %2e%2e or %252e.

If you find signs of compromise, delete every dropped file you found, then go further than the malware itself. Rotate your WordPress secret keys and salts in wp-config.php (AUTH_KEY, SECURE_AUTH_KEY, LOGGED_IN_KEY, NONCE_KEY and their _SALT counterparts), which invalidates any session cookies an attacker forged or stole. Reset every admin password and review the Users list for accounts you did not create. Audit installed plugins and themes for anything you do not remember adding, since a webshell with file-manager access is often used to plant a second, quieter backdoor before the first one is found.

Our web application penetration tests test the kind of request-handling logic that let this bug reach outside its own directory, and our attack surface management work tracks the exact core, plugin and theme versions running across a fleet of sites so a release like 7.1.2 does not depend on someone remembering to check. Open the chat and Yaali, our AI agent, will pass your question to the engineer who would do the work.


Sources: WordPress.org, 7.1.2 release notes, Patchstack, attackers started probing hours after the patch, Patchstack, WordPress 7.1.2 security release, Security Affairs, Equixly, Help Net Security, Aviatrix Threat Research Center, CISA, KEV catalog addition.

Read next

Back to the blog, or tell us about your system in the chat. Yaali, our AI agent, answers first and brings in an engineer.