Site Security
Estimated reading time: 9 min read

WordPress 7.1.2: Check for CVE-2026-87902 Now

Ben Marshall Ben Marshall Full-stack Developer & Engineering Manager
Glowing red warning triangle inside a network of connected nodes, illustrating the CVE-2026-87902 WordPress core vulnerability
On this page
WordPress 7.1.2 fixes CVE-2026-87902, an unauthenticated flaw attackers began probing within hours. Update, then check your theme, PHP settings, and logs.

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

  1. 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]
  2. Check whether the active theme, and its parent theme, has a top-level folder whose name starts with page-.[3]
  3. Check register_argc_argv for the web server’s PHP, not the command line.
  4. Search access logs for traversal in the pagename parameter and for pearcmd, and look for unexpected .php files in /tmp and /var/tmp.[4][5]
  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 as page-templates. That’s a common way to organize page templates, so plenty of themes have one.
  • PHP runs with register_argc_argv enabled. 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 --minor

Many 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
BranchFirst patched release
7.17.1.2
7.07.0.6
6.96.9.9
6.86.8.10
6.76.7.9
6.66.6.9
4.7 to 6.5See the full list on WordPress.org[2]
4.6 and earlierNo 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/null

No 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' -ls

On 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 = Off

Test 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

What is CVE-2026-87902?

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.

Which WordPress versions fix CVE-2026-87902?

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.

Is my site vulnerable if my theme has no page- folder?

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.

How do I know if my WordPress site was attacked?

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.

Does a firewall or security plugin make updating unnecessary?

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