WordPress 7.1.2: Check for CVE-2026-87902 Now

On this page
- TL;DR: What to do right now
- What CVE-2026-87902 is, in plain language
- Step 1: Confirm every site is on a patched release
- Step 2: Check whether a site was exposed before you patched
- Step 3: Look for probing and payloads
- Can't update today? Temporary stopgaps
- After the patch: shrink the next window
- Sources
- Frequently Asked Questions
If you run WordPress, update to 7.1.2 (or the patched release for your branch) today. CVE-2026-87902 lets an unauthenticated attacker make WordPress include a local PHP file from outside the theme folders, and on some servers that turns into remote code execution.[1][3] Attackers started probing sites within hours of the patch.[4][5]
The update fixes the bug. What it doesn’t tell you is whether a site was exposed before you patched it, and that’s what site owners and agencies need to know. This is the checklist we’d run: confirm versions across every site, check the two conditions that turn this bug into code execution, then search logs and temp folders for signs someone tried.
TL;DR: What to do right now
- Update every site to 7.1.2, or the patched release for its branch (7.0.6, 6.9.9, 6.8.10, and so on down to 4.7.37).[2]
- Check whether the active theme, and its parent theme, has a top-level folder whose name starts with
page-.[3] - Check
register_argc_argvfor the web server’s PHP, not the command line. - Search access logs for traversal in the
pagenameparameter and forpearcmd, and look for unexpected.phpfiles in/tmpand/var/tmp.[4][5] - If you find payload files, treat the site as compromised and run a full cleanup before anything else.
What CVE-2026-87902 is, in plain language
When a page is requested, WordPress works out which page template to load. The bug is in that page-template resolution: under certain conditions, an unauthenticated visitor can make it include a chosen, readable PHP file from outside the active theme’s directories.[1] The fix changed one file, wp-includes/template.php, and WordPress backported it to every branch back to 4.7.[2]
Patchstack scores it 9.2 on CVSS 4.0.[3] WordPress.org’s advice is blunt: “update your sites immediately.”[1]
Update, Sep 25: Singapore’s Cyber Security Agency says the flaw is reportedly being actively exploited and that a proof-of-concept exploit is publicly available.[9] It lists a CVSS v3.1 score of 8.1.[9] That isn’t a contradiction of Patchstack’s 9.2: the two figures use different CVSS versions. The advice is the same either way: update.
When does it become code execution?
Including a file isn’t the same as running an attacker’s code. Patchstack describes the chain that gets there:[3]
- The theme has a top-level
page-folder, such aspage-templates. That’s a common way to organize page templates, so plenty of themes have one. - PHP runs with
register_argc_argvenabled. Patchstack notes that’s the default in the official PHP Docker images and in cPanel environments on PHP below 8.5.[3] - A usable PHP file exists on the server. The well-known one is PEAR’s
pearcmd.php, which attackers use to write a new PHP file to disk.[3][4]
Take away any one of those and the known chain breaks. The underlying bug is still there, so the answer doesn’t change: update.
Why “we’ll patch this week” is too slow
Patchstack logged the first exploitation attempts on September 22, the day the patch shipped, and the first attempt to write a file through pearcmd a few hours later.[4] Reporting on Patchstack’s data, BleepingComputer said related traffic grew tenfold the next day and included a write-to-disk stage.[5] Attackers also send the payload by POST, not just GET, and WordPress reads pagename from the POST body first.[4] That detail matters for the stopgaps below.
Step 1: Confirm every site is on a patched release
Sites with automatic background updates enabled should already have the fix.[1] “Should” is doing a lot of work there. Hosts, plugins and well-meaning developers switch auto-updates off all the time, so check rather than assume.
One site
Dashboard → Updates shows the running version. With WP-CLI:
wp core version
wp core check-update
# current sites: update to 7.1.2
wp core update
# older branch: take the latest release in that branch
wp core update --minorMany sites (agencies)
If you manage a fleet, loop over the installs, or use wp @all core version if you’ve defined WP-CLI aliases. Anything not on a release from this table needs attention:
for dir in /var/www/*/public; do
printf '%s ' "$dir"; wp --path="$dir" core version
done| Branch | First patched release |
|---|---|
| 7.1 | 7.1.2 |
| 7.0 | 7.0.6 |
| 6.9 | 6.9.9 |
| 6.8 | 6.8.10 |
| 6.7 | 6.7.9 |
| 6.6 | 6.6.9 |
| 4.7 to 6.5 | See the full list on WordPress.org[2] |
| 4.6 and earlier | No fix. Upgrade to a supported release. |
Step 2: Check whether a site was exposed before you patched
Does the active theme have a page- folder?
Check the child theme and its parent. From the WordPress root:
theme=$(wp eval 'echo get_stylesheet_directory();')
parent=$(wp eval 'echo get_template_directory();')
ls -d "$theme"/page-*/ "$parent"/page-*/ 2>/dev/nullNo output means neither theme has a top-level page- folder, which rules out the known chain for that site.
Is register_argc_argv on for the web server?
Don’t rely on php -i on the command line here. You need the value the web server’s PHP actually uses. Your host’s PHP settings screen usually shows it. If not, create a throwaway file with a random name, load it once, and delete it:
# run from the WordPress root;
# replace example.com with the site's domain
name="argc-$(openssl rand -hex 8).php"
cat > "$name" <<'PHP'
<?php echo ini_get('register_argc_argv') ? 'on' : 'off';
PHP
curl -s "https://example.com/$name"; echo
rm "$name"The PHP manual lists the default as on, and the setting is deprecated as of PHP 8.5.[6] Many sites will see “on”.
Step 3: Look for probing and payloads
Access logs
Patchstack’s indicators are encoded traversal such as %2e%2e in the pagename parameter, requests that reference pearcmd, and the +config-show and +config-create commands.[4] Some tools identify themselves (cve-2026-87902-poc/1.0, nuclei-cve-2026-87902/1.0), but don’t rely on user agents.[4]
# adjust the path for your server or host
LOG=/var/log/nginx/access.log
grep -Ei 'pagename=[^& ]*(\.\.|%2e|%252e)' "$LOG"
grep -Ei 'pearcmd|config-create|config-show' "$LOG"
grep -Ei 'cve-2026-87902' "$LOG"Access logs don’t record POST bodies, so a clean result is not proof of a clean site. It only rules out the GET variants.
Files in /tmp and /var/tmp
The payloads land in /tmp and /var/tmp, with names like wp-pear-rce-flag.php and poc87902.php.[5] Update, Sep 24: later reporting adds files named luci_ or zeta_ followed by random characters, and requests that fetch a PHP uploader script from GitHub.[8] The find command below lists every recent .php file, so it catches those too.
find /tmp /var/tmp -maxdepth 2 -name '*.php' \
-newermt '2026-09-22' -lsOn shared hosting you may not be able to see the system temp folders, so ask your host to run this check. If anything turns up, treat the site as compromised: take it out of rotation, rotate credentials and keys, then clean up. Our WordPress compromise cleanup checklist covers the order of operations.
Can’t update today? Temporary stopgaps
Some sites can’t update right away: a frozen client site, an ancient PHP version, a change freeze. Patchstack calls rejecting traversal in pagename “an effective stopgap”, and notes that disabling register_argc_argv breaks the pearcmd chain without fixing the inclusion.[4]
Reject traversal in pagename (GET and POST)
A web server rule that only inspects the query string misses the POST variant. A must-use plugin runs inside WordPress after it has read pagename from either source:
<?php
/**
* Plugin Name: Temporary CVE-2026-87902 stopgap
* Description: Rejects path traversal in the pagename query
* var (GET or POST). Delete this file once the
* site runs a patched WordPress release.
*/
add_filter( 'request', function ( $vars ) {
if ( ! isset( $vars['pagename'] ) ) {
return $vars;
}
$value = (string) $vars['pagename'];
// Undo single, double and triple URL encoding.
for ( $i = 0; $i < 3; $i++ ) {
$value = rawurldecode( $value );
}
foreach ( array( '..', '\\', "\0" ) as $needle ) {
if ( false !== strpos( $value, $needle ) ) {
status_header( 400 );
exit( 'Bad request' );
}
}
return $vars;
}, 0 );Save it in wp-content/mu-plugins/ as cve-2026-87902-stopgap.php, click through a few normal pages to confirm nothing breaks, and delete it once the site is patched.
Turn off register_argc_argv
It’s a per-directory setting, so on most hosts you can set it in php.ini or in a .user.ini file in the site root:[6]
register_argc_argv = OffTest after changing it. Very old code sometimes reads $_SERVER['argv'] on web requests.
After the patch: shrink the next window
The patch shipped, and attackers were probing the same day. Sites with automatic background updates got the fix without anyone lifting a finger.[1] A few habits make the next one boring:
- Leave automatic core security updates on. If a host or plugin disabled them, turn them back on, or own the patch window explicitly.
- Keep an inventory of WordPress version, PHP version and active theme per site, so a check like this takes minutes, not an afternoon.
- Treat the IPs in your logs as hostile everywhere, not just on this URL: block or rate-limit them on logins and forms too.
Zero Spam won’t patch a core vulnerability. Updating does that. It handles the next layer: the free Zero Spam for WordPress plugin blocks spam and malicious submissions across logins, registrations and forms, and our guide to stopping WordPress spam fast shows the setup. If you want IP reputation and threat confidence inside your own code or across client sites, start on the free Explorer API plan and move up when you need more lookups. Code ZEROSPAM20 takes 20% off a paid license.[7]
Frequently Asked Questions
CVE-2026-87902 is a critical WordPress core flaw in page-template resolution. It lets an unauthenticated attacker include a local PHP file from outside the theme folders, and on some servers that leads to remote code execution. WordPress fixed it in 7.1.2 on September 22, 2026.
WordPress 7.1.2 fixes it. The fix was backported to every branch back to 4.7, including 7.0.6, 6.9.9, 6.8.10 and 4.7.37. Versions 4.6 and earlier get no fix and need a full upgrade.
The known code execution chain needs a top-level theme folder starting with page-, PHP’s register_argc_argv enabled, and a usable PHP file such as pearcmd.php. Without the folder, that chain doesn’t work. Update anyway, because the core bug is still there.
Search access logs for traversal sequences in the pagename parameter and for requests mentioning pearcmd. Look for unexpected .php files in /tmp and /var/tmp. Access logs don’t capture POST bodies, so a clean log is not proof of a clean site.
No. A firewall rule or a temporary pagename filter can reduce risk for a few days. Only a patched WordPress release removes the vulnerability.
Sources & References
- WordPress.org News: WordPress 7.1.2 security release (Sep 22, 2026)
- WordPress.org Documentation: Version 7.1.2 fix and backported branch versions (Sep 22, 2026)
- Patchstack: WordPress 7.1.2 unauthenticated LFI to RCE preconditions (Sep 22, 2026)
- Patchstack: attackers started probing WordPress sites hours after the patch (Sep 22, 2026)
- BleepingComputer: hackers start exploiting critical WordPress flaw for code execution (Sep 23, 2026)
- PHP Manual: register_argc_argv default, INI_PERDIR, deprecated in PHP 8.5 (accessed Sep 23, 2026)
- Zero Spam: API plans, from the free Explorer tier up (accessed Sep 23, 2026)
- The Hacker News: attackers exploit WordPress CVE-2026-87902 within hours, payload file names (Sep 24, 2026)
- Cyber Security Agency of Singapore: active exploitation of CVE-2026-87902, public PoC, CVSS v3.1 8.1 (Sep 24, 2026)
