wp2shell WordPress RCE: How to Patch and Check for Backdoors
On July 17, 2026, WordPress pushed emergency releases across every supported branch of core. Within days, one honeypot network alone logged tens of thousands of exploitation attempts and more than a hundred backdoor admin accounts planted across compromised sites.
The vulnerability is called wp2shell: two bugs chained together in WordPress core, no plugins involved, that let an anonymous visitor execute code on a default install. If you're on WordPress 6.9.0 through 7.0.1 without 7.0.2 or 6.9.5 applied, this is today's most urgent task.
What is wp2shell, exactly?
wp2shell is the name researchers gave to an exploit chain combining CVE-2026-63030 and CVE-2026-60137, two separately tracked bugs in WordPress core that together reach unauthenticated remote code execution. Neither bug alone is a full compromise.
But when chained, they are a problem.
The first bug lives in the REST API's batch endpoint, the route that lets one HTTP request bundle several sub-requests together. A parsing mistake there throws off an internal counter, so a later sub-request ends up running under the wrong handler with looser permission checks than it should have.
That mismatch reaches the second bug: a parameter in WP_Query, the class that builds most of WordPress's database queries, which skips its own sanitization when handed a string instead of the array it expects. Chain the two and an anonymous request injects SQL, then executes PHP on the server.
This sits entirely in core WordPress. It isn’t a plugin, or a theme, or even a page builder. Right now, a stock install, with zero plugins and zero custom configuration, is fully exploitable.
The affected ranges split by branch:
- WordPress 6.8.0 to 6.8.5: the SQL injection only, patched in 6.8.6
- WordPress 6.9.0 to 6.9.4: the full chain, patched in 6.9.5
- WordPress 7.0.0 to 7.0.1: the full chain, patched in 7.0.2
- WordPress 7.1 beta2: both fixes already included
Versions before 6.8 aren't exposed to either bug.
How dangerous this is depends on which record you check. WordPress's own advisory calls the chain critical, while public CVE trackers disagree with each other, some listing a 7.5, others scoring the injection above 9. The fact is: unauthenticated code execution on core with a public working exploit doesn't need a CVSS score to justify urgency.
How do you fix it
Update to WordPress 7.0.2, 6.9.5, or 6.8.6, whichever branch you're on, today. That single action closes both vulnerabilities and removes any need for the workarounds below.
Check your version in wp-admin under Dashboard, then Updates, or run wp core version over WP-CLI. Seeing 6.9.0 through 6.9.4 or 7.0.0 through 7.0.1 means you're exposed to the full chain right now.
WordPress pushed this fix through its forced auto-update mechanism, which usually means affected sites update themselves within hours even with regular auto-updates turned off. Don't assume that it reached every site you're responsible for. Hosts that disable core auto-updates, or security plugins blocking outbound connections to WordPress's update servers, won't have received it. You need to log in and check each one.
Skip the staging-first discipline I usually recommend for routine updates, at least for this one. That workflow exists to catch plugin conflicts before they reach production. An unauthenticated RCE with a public working exploit changes the math: patch production immediately, then verify nothing broke, instead of the other way around.
Managing several client sites and can't patch all of them within the hour? Triage internet-facing sites with public REST usage first, password-protected staging environments last.
If you can't patch immediately
If patching has to wait a few hours because you're waiting on hosting support or need to test a WooCommerce integration first, there are stopgaps. None of them replace the update.
At your WAF or reverse proxy, block requests to both /wp-json/batch/v1 and the query-string equivalent, ?rest_route=/batch/v1. Blocking only one leaves the door open through the other. If your site sits behind Cloudflare, confirm the managed ruleset covering this chain is active. Wordfence's rule set covers the same pattern, if that's what you're already running.
Disabling the WordPress REST API entirely, through a plugin like Disable WP REST API, blocks the vulnerable route along with everything else that depends on REST: the block editor, application passwords, the mobile app, and most plugins built after 2018, so treat it as an emergency brake for a few hours.
That's especially true for a WooCommerce store, where subscription renewals and payment gateway webhooks lean on that same API surface. I've written before about what happens when that surface misbehaves for entirely unrelated reasons.
How to check if you were already hit
Before treating a successful patch as the end of the story, check whether an attacker got there first. Public exploit code has circulated since July 18, and mass scanning started within days.
Start with wp-content/mu-plugins/. Must-use plugins load automatically and never appear on the Plugins screen in wp-admin, making that folder a favorite drop point for a backdoor that creates its own admin account. If you see a file there you didn't put, screenshot it and pull server logs around its creation time before touching anything.
If you have WP-CLI, run:
wp core verify-checksums
Any line reporting a file that doesn't verify against checksum is a file that differs from the official WordPress release, and worth opening immediately.
Then check for the kind of code a webshell typically hides behind:
grep -RilE "eval\(|base64_decode\(|shell_exec\(|passthru\(|assert\(" \
wp-content/uploads wp-content/plugins
A hit doesn't automatically mean compromise, since some legitimate plugins use base64 encoding for ordinary reasons, but it's a list worth reading line by line.
Finally, check Users in wp-admin for any administrator account you don't recognize, and scan wp-content/plugins for a folder you didn't install, especially one mimicking a security tool's name. Reported campaigns have disguised a full attack panel as a legitimate-looking security scanner plugin.
Why keeping WordPress updated is not optional
WordPress powers a large share of the web, and automated scanning tools don't distinguish a hobby blog from an enterprise storefront. They scan address ranges, not judgment calls. A core vulnerability with a public proof of concept gets probed within days of disclosure, sometimes hours. wp2shell went from disclosure to public exploit code to observed exploitation in roughly 48 hours.
The uncomfortable detail: the batch REST endpoint has shipped since version 5.6 in 2020, but the route-confusion bug only entered core with version 6.9 in December 2025. Every site exposed to the full chain was running a release less than eight months old.
Staying current is still what closes this fastest, because the fix ships to the same version number you're already supposed to be running. I've laid out what a real update cadence looks like for the plugin and theme layer. Core security releases deserve the same discipline, just on a much faster clock.
What a hardened WordPress security stack actually looks like
Patching wp2shell closes one specific door. A WordPress install has several others, and most compromises come through configuration rather than a zero-day.
Start at the application layer. Wordfence's firewall runs at the WordPress level and catches attack patterns like this one even before a core patch exists, since it works off request behavior rather than version numbers. Run it alongside Cloudflare's WAF: the CDN layer stops obvious junk before it reaches your server, and the application layer catches what's specific to WordPress.
Enforce two-factor authentication on every account with publish or administrator access. Wordfence and WP 2FA both handle this without much configuration.
At the file level, add define('DISALLOW_FILE_EDIT', true); to wp-config.php. This removes the plugin and theme editor from wp-admin, one less way for a stolen admin session to drop code through the dashboard. Lock wp-config.php itself to 440 or 400 permissions where your host allows it, since that file holds database credentials in plain text.
On the database side, the WordPress user your site connects with rarely needs FILE privilege, and removing it closes off an entire category of SQL-injection-to-code-execution chain, including the mechanism this vulnerability used. Ask your host or DBA to check the account's grants.
If you're not using XML-RPC, and most sites aren't unless publishing from a mobile app or a service like IFTTT, disable it at the server level.
One more thing worth knowing: sites running a persistent object cache, Redis or Memcached, were less exposed to the code-execution half of this specific chain, per Cloudflare's own writeup on the incident. That's a side effect of caching architecture, not a security feature, so don't treat it as a substitute for patching. A proper caching layer belongs in your stack anyway, for reasons that have nothing to do with security.
Where this leaves you
Running WordPress 6.9.0 through 7.0.1? Stop here and patch to 7.0.2 or 6.9.5 first. On 6.8.x? Update to 6.8.6 for the SQL injection fix, even though you were never exposed to the RCE half. Already patched? Spend twenty minutes checking mu-plugins and running a checksum verify anyway. Patched and clean aren't the same claim.
wp2shell won't be the last core vulnerability that turns a default install into a liability overnight. The sites that come out fine are the ones where updating is a standing habit, not a task someone remembers during a panic.
If you're responsible for client WordPress sites and want someone checking for exactly this kind of thing before it becomes a 2am phone call, that's the maintenance and security work I do. Get in touch.