CVE-2026-87902: Was Your WordPress Site Already Hit?

unflagdomain Team·UPDATED September 23, 2026

CVE-2026-87902 is a critical flaw in WordPress core, fixed in 7.1.2 on 22 September 2026, that let an unauthenticated visitor make your site include a local PHP file of their choosing. Updating closes the door. It does not tell you whether anyone walked through it first. This guide shows how to check, what to remove if they did, and why a blocklist flag can turn up weeks after the update.

TL;DR: Every WordPress version from 4.7.0 to 7.1.1 is affected, but turning the bug into code execution needs two things on your server: a theme with a top-level directory whose name starts with page- (the advisory names Twenty Twelve, Twenty Fourteen, Neve, Hestia and Sydney) and a readable local PHP file that does something dangerous when included, in practice PEAR's pearcmd.php with register_argc_argv switched on, which is the default in the official PHP Docker image and in cPanel on PHP older than 8.5 (WordPress advisory, 2026). Attackers were probing for it less than five hours after the patch shipped (Patchstack, 2026). Update first, then search your access log for pagename= requests carrying traversal sequences since 22 September. A 200 response with OPML output where a page should have been means the inclusion worked.

This is written for the owner who has already updated, or is about to, and wants to know whether the site was touched in the window before that. It also covers the part the security news skips: what happens to your domain's reputation if it was.

What is CVE-2026-87902?

When WordPress renders a page, it looks for a template named after that page's slug. Inside get_page_template() the slug, which WordPress reads from the pagename query variable and therefore straight from the request, was used to build a candidate filename:

$templates[] = "page-{$pagename}.php";

The value was URL-decoded first, and unlike a sibling code path three lines above it, this branch never went through validate_file() (Patchstack, 2026). A pagename containing ../ sequences therefore walked out of the theme directory and made WordPress include whatever PHP file the path pointed at. Two constraints shaped the attack: WordPress appends .php itself, and the path has to start with page-. That second constraint is why the theme matters. The traversal has to begin inside a real directory called page-something, which is exactly what a page-templates folder is.

WordPress rates it critical, CVSS 4.0 score 9.2, class CWE-98 (local file inclusion). It was reported by Robert Ressl, who has since published a working proof of concept (GitHub, 2026). WordPress shipped 7.1.2 as a security-only release with this single fix and, as a courtesy to sites on older branches, backported it all the way down to 4.7, so 4.7.37, 5.0.29, 6.8.10 and every branch in between received the same change (WordPress, 2026). Sites with automatic background updates enabled were patched without anyone logging in.

The fix does two things: it adds the missing validate_file() check, and it introduces a containment function, _wp_is_template_path_allowed(), that refuses any resolved template outside the stylesheet, template or theme-compat directories (Patchstack, 2026). The second layer is the reason the same shape of bug will be harder to reintroduce.

Is my site actually exploitable?

Only if both preconditions hold. Including a local file on its own lets an attacker run PHP that was already on your server, which is dangerous but limited. It is the second condition that turns it into arbitrary code execution.

1. Your active theme has a top-level directory starting with page-. Check the parent and the child theme, because either one qualifies:

ls -d wp-content/themes/*/page-*/

The advisory names Twenty Twelve, Twenty Fourteen, Neve, Hestia and Sydney. Between them those five themes report roughly 450,000 active installs on WordPress.org: Neve 200,000, Sydney 80,000, Hestia 70,000, and 50,000 each for the two legacy default themes (WordPress.org theme directory, September 2026). The list is not exhaustive. A page-templates folder is a common convention, so any theme that uses it qualifies whether or not it appears in a headline.

2. A readable PHP file on the server that does something dangerous when included. The known route is pearcmd.php, part of the PEAR package manager. When PHP's register_argc_argv setting is on, that file reads its arguments from the request and acts on them, which is the well-documented path from file inclusion to command execution. The official PHP Docker image ships that way, and so does the default cPanel configuration on PHP older than 8.5 (WordPress advisory, 2026). To check:

find / -name pearcmd.php 2>/dev/null
php -i | grep register_argc_argv

One caveat: php -i reports the command-line configuration, and the web server's PHP can differ. Your host's PHP settings panel, or a temporary phpinfo() page you delete afterwards, shows the value that actually matters.

If either condition is missing, your exposure was probes at most. Update anyway. The proof of concept is public, and the next local file worth including may not be pearcmd.php.

Was my site probed or exploited?

The timeline is short. WordPress published 7.1.2 on 22 September 2026. Patchstack logged the first probes at 17:44 UTC the same day, less than five hours later, from three IP addresses, with activity peaking in the first hour (Patchstack, 2026). What they saw was fingerprinting: requests that made WordPress include harmless core files to confirm the traversal worked, building a list of sites where it did. The step that matters, pointing the same primitive at pearcmd.php, had not appeared in their data at the time of writing. With a proof of concept on GitHub, that is not a state that lasts.

Search your access log from 22 September onward for these patterns:

  • A pagename parameter carrying traversal sequences. The observed probes double-encoded the dots as %252e%252e; also look for %2e%2e, ..%2f and plain ../. Traversal depth varied from three to seven levels.
  • A page_id of a real page, combined with a pagename that names a core file. The probes tried to include wp-links-opml, wp-includes/functions and wp-cron, sent to the site root and to /index.php directly.
  • POST requests, not only GET. WordPress reads pagename from the POST body too, and most access logs do not record bodies. A clean grep therefore lowers your suspicion; it does not clear you.
grep -iE 'pagename=[^ &]*(%252e%252e|%2e%2e|\.\./)' /path/to/access.log

How to read a hit: a 404 or an ordinary page means the inclusion failed. A 200 response containing OPML, an XML list of links where a page should have been, means it worked and your site was on somebody's list of exploitable targets (Patchstack, 2026). Any request mentioning pearcmd means you should assume compromise and skip to the next section.

Then check the files themselves, whatever the log said. Logs rotate, and an attacker who got in does not leave a tidy record:

  • Core file integrity. With WP-CLI, wp core verify-checksums lists every core file that differs from the official release.
  • PHP files created or changed since the patch date, especially in wp-content/uploads, wp-content/mu-plugins and any cache directory: find wp-content -name '*.php' -newermt '2026-09-22'.
  • Administrator accounts you did not create, and scheduled tasks you do not recognise (wp cron event list).
  • Changes to .htaccess, wp-config.php and the active theme's functions.php, the usual places for a persistence hook.

Our walkthrough of the signs your website has been hacked covers the symptom side, which is worth reading too, because the first sign is frequently a visitor's browser warning rather than anything you find yourself.

What to do if you were hit

Treat it as a full compromise, in this order:

  • Update to 7.1.2 first, or to your branch's backport. Cleaning before the door is closed means cleaning again.
  • Take a backup, even an infected one, before touching anything.
  • Remove what the checks above surfaced: restored core files from a fresh download, deleted webshells in uploads and mu-plugins, injected code stripped from functions.php, .htaccess and wp-config.php.
  • Rotate every credential: WordPress passwords for all users, hosting control panel, SFTP, the database user, API keys stored in the site, and the authentication salts in wp-config.php, which forces every session to log out.
  • Re-scan with a reputable security plugin and confirm it reports clean. Our guide to WordPress malware removal covers the scan-and-verify loop, and cleaning a hacked website covers the wider sequence for any platform.
  • Close the second precondition while you are there. If PEAR is not something you use, remove pearcmd.php; otherwise set register_argc_argv to Off for the web server's PHP. That neutralises this chain and the next inclusion bug that finds the same file.

Why a blocklist flag can show up weeks later

Code execution on a WordPress site is rarely used to deface it. It is used to make money quietly: injected SEO spam pages, redirects that only fire for visitors arriving from a search engine, a phishing kit dropped into a subfolder, or a loader that serves malware to a fraction of visitors. Every one of those is designed to stay invisible to the owner and visible to a crawler, and security vendors' crawlers are exactly who finds them.

That produces a delay. The exploit happens in one week, the payload runs for a while, a vendor's crawler records what your domain served, the flag goes live, and eventually a customer mentions that their browser turned red. In our experience running unflagdomain.com, by the time an owner notices a flag the domain has usually tripped several lists, because vendors trade reputation data and one verdict ripples into the next. The flags then sit there. They do not expire when you update WordPress, and most vendors do not re-check unless asked.

So after the cleanup, look at your reputation deliberately rather than waiting to hear about it. Our free blocklist check looks the domain up across security vendors and shows which ones are flagging it, with no signup. Be clear about what it is and is not: it is a reputation check, not a malware scan. A clean result does not prove a clean server. A flagged result tells you a vendor already saw something, and that cleanup alone will not undo it. The guide on how to check if your domain is blacklisted explains what the different lists mean.

How do I get the flags removed once the site is clean?

You ask each flagging vendor to re-check the domain, one at a time, because there is no central registry. For Google the step is manual and cannot be automated: open Google Search Console, find the Security Issues report, confirm the fix, and request a review. Google publishes no API for it. Every other vendor has its own false-positive form or security inbox and its own timeline, which our guide on how long blacklist removal takes sets out honestly.

Do not start before the site is clean. Vendors re-scan when they review a request, and a site that still carries the payload fails the re-scan and keeps the flag.

Once you are clean, the rest is administrative: work out who is flagging you, find each vendor's correct contact, and write a request that says what happened and what you fixed. That is the gap unflagdomain.com fills. After the free check shows who is flagging you, we dispatch a removal request to each flagging vendor for a one-time €39: plain text, written separately for each vendor so they do not read as bulk mail, with your own address as Reply-To so replies land in your inbox. Our catalog covers 133 active vendors, and Google Safe Browsing stays a guided manual step through Search Console because it has to.

The limits, stated plainly: we do not scan for or clean malware, so the cleanup above is yours or your security provider's. And we guarantee the dispatch, never the delisting, because every vendor decides on its own. If the re-scan after payment finds zero flags, the payment is refunded automatically.

What if I cannot update right now?

Updating is a one-click change and nothing below replaces it. If a staging process or a plugin conflict genuinely holds you back for a day or two:

  • Turn on automatic background updates for core so the next security release does not depend on you reading the news.
  • Put a web application firewall rule in front of the site that blocks any request whose pagename contains traversal sequences. Managed firewalls, including Patchstack's, shipped such a rule on release day.
  • Remove the second precondition: delete pearcmd.php if you do not use PEAR, or set register_argc_argv to Off for the web server's PHP.
  • Do not rename the theme's page- directory as a shortcut. Templates that live there stop resolving, and the page breaks in a way that looks like the hack you were trying to prevent.

Our guide on WordPress hardening covers the standing measures that make the next disclosure a non-event.

Updated is not the same as untouched

The news cycle around a core vulnerability ends when the patch ships. Your exposure ends when you can say what happened on your server between the disclosure and the update. For most sites that is a five-minute check of one log and a file listing, and the answer is nothing. For the sites where the answer is something, the cost of finding out now is a cleanup; the cost of finding out in a month is a cleanup plus a domain that a dozen security vendors have already made up their minds about.

// FAQ
  • The advisory names those themes because each ships a top-level directory starting with page-, which is one of the two preconditions. The bug itself is in WordPress core, not the theme, so the fix is updating WordPress to 7.1.2 or your branch's backport. The theme needs no update of its own once core is patched.

  • Going forward, yes: the update closes the inclusion path. It does not undo anything that happened before it. Search your access log from 22 September 2026 for pagename parameters containing traversal sequences, and check for PHP files created or changed since then. A successful probe returns a 200 response with OPML output where a page should have been.

  • It is the fingerprint of the probes Patchstack observed within hours of the patch. Attackers made WordPress include wp-links-opml.php, a harmless core file, to confirm the traversal worked. A 404 or a normal page means it failed. A 200 response carrying OPML means it succeeded and your site was on a list of exploitable targets.

  • No. The update replaces core files with clean copies, which removes modifications to those files only. Webshells in uploads or mu-plugins, injected code in the theme, rogue administrator accounts and scheduled tasks all survive an update. Treat any confirmed exploitation as a full compromise and clean it as one.

  • No. Blocklist flags record what your domain served, and they stay until each vendor re-checks. Remove the payload first, confirm the site is clean, then request a review from every flagging vendor. Google Safe Browsing requires a manual review request in Search Console; other vendors each have their own false-positive process.