If you run WordPress and you've been ignoring update notifications, this is the week to stop. In mid-July 2026, researchers disclosed a vulnerability chain nicknamed wp2shell, a pair of bugs baked into WordPress core (not a plugin, not a theme) that let a completely anonymous attacker execute code on a default install with a single HTTP request. No login. No plugins required. No social engineering. Just a request. (The Hacker News, Wiz Research)
Let's break down what actually went wrong, who's affected, and what to do about it.
The quick version
- What: Two chained WordPress core vulnerabilities,
CVE-2026-63030 (a REST API logic bug) and CVE-2026-60137 (a SQL injection), that together allow unauthenticated remote code execution.
- Who's exposed: Any site running WordPress 6.9.0-6.9.4 or 7.0.0-7.0.1. Sites on 6.8.x get hit by the SQL injection alone (no RCE). Fixed versions are 6.8.6, 6.9.5, and 7.0.2.
- Found by: Adam Kues at Assetnote (Searchlight Cyber's attack-surface team), reported through WordPress's HackerOne program. The SQL injection piece was reported separately by other researchers.
- Status: Patches shipped, WordPress forced auto-updates where it could, and a working proof-of-concept is now public on GitHub. Scanning and exploitation attempts are already happening in the wild. (Bleeping Computer, The Hacker News)
What actually went wrong (the fun technical part)
This is a genuinely clever bug chain: two flaws that are individually not that scary, but stacked together, are game over.
Bug #1, the SQL injection. Inside WP_Query, there's a parameter called author__not_in. It's supposed to only ever be an array of user IDs. The code that sanitizes it checks "is this an array?" and if you hand it a string instead, that check gets skipped entirely, dropping your raw string straight into the SQL query. Classic type-confusion injection. This bug alone goes back to WordPress 6.8.
Bug #2, the route confusion. Here's the part that turns a boring injection into an unauthenticated disaster. WordPress has had a REST API "batch" endpoint since 5.6 (/wp-json/batch/v1) that lets you bundle multiple sub-requests into one call. (Rapid7) Internally, it tracks those sub-requests using two parallel arrays, one for validation, one for matched routes and handlers.
The bug: if one sub-request fails a specific internal check (wp_parse_url() failing on a malformed path), the error gets pushed into the validation array but not the matches array. That single off-by-one desync means the batch processor's bookkeeping falls out of sync, and a later sub-request ends up executing under a different request's permissions and handler than the one it was actually validated against.
Chain them together: use the batch endpoint's confusion to sneak past the permission and allow-list checks, land your attacker-controlled input in the vulnerable author__not_in parameter, and you've got unauthenticated SQL injection that unfolds into full remote code execution, all in one HTTP request, on a completely default WordPress install with zero plugins.
That's the part that makes this different from the usual "some plugin had a bug" story. This is core code, present in a stock install.
One mitigating detail: the full RCE path requires the site to not be running a persistent object cache (like Redis or Memcached), per Cloudflare. Default installs don't have one, so the default-install exposure is real, but sites already using persistent object caching for performance may have accidentally sidestepped this one. That's a side effect, not a fix, and it doesn't help the SQL injection piece at all. (Rapid7)
Why the severity scoring is a little misleading
Here's a genuinely interesting wrinkle: WordPress's own advisory rates the RCE chain "Critical," but the official CVE record for the route-confusion bug (CVE-2026-63030) only scores 7.5 (High), because the scoring formula credits it for data-access impact, not the code-execution impact you'd actually get once it's chained. Meanwhile the SQL injection CVE scores over 9.1. So don't just skim severity labels here: both CVEs matter, and the "less critical-looking" one is the piece that makes the whole chain unauthenticated. (The Hacker News)
Real-world impact
- Full site takeover. Code execution means an attacker can drop a webshell, create rogue admin accounts, or install a fake "plugin" that's really malware.
- No warning required. Because it's unauthenticated and hits a stock install, there's no login page to brute-force, no plugin to guess, just a crafted request against any exposed WordPress site.
- Scale. WordPress powers an enormous share of the web (estimates put the total install base north of 500 million sites). The exposed subset is smaller, only sites on 6.9+ released since December 2025, but that's still every site that updated normally over the past several months, which is exactly the behavior you want people to have. (The Hacker News)
- This isn't a one-off. It comes on the heels of other recent WordPress incidents this year: a critical Everest Forms Pro RCE actively exploited since April (Cybersecurity News), a Gravity SMTP bug leaking API keys and OAuth tokens with 17M+ blocked attempts (The Hacker News), and a supply-chain compromise of ShapedPlugin's premium plugins (The Hacker News). WordPress's plugin ecosystem and now its core have both had a rough 2026.
How to protect your site
- Update WordPress now. Fixed versions: 6.8.6, 6.9.5, or 7.0.2 (7.1 beta already has both fixes). WordPress pushed forced auto-updates where possible, but don't assume it landed, actually check your version in wp-admin.
- If you can't update immediately, block anonymous access to the batch endpoint at your firewall/WAF:
- Block both
/wp-json/batch/v1 and the query-string form ?rest_route=/batch/v1. A rule that only covers one path leaves the other route wide open.
- Cloudflare's managed WAF rules already cover this if you're behind Cloudflare.
- Alternatively, disable the WP REST API for anonymous users entirely (there's a plugin for that), or drop in a small snippet that rejects unauthenticated
/batch/v1 requests via rest_pre_dispatch. (The Hacker News)
- Check for signs of compromise even after patching: unexpected admin accounts, unfamiliar plugins you didn't install, and weird entries in your access logs hitting
/wp-json/batch/v1 before you patched.
- General hygiene that keeps paying off: keep auto-updates on, minimize your plugin footprint (fewer plugins means a smaller attack surface), and if you're not using a persistent object cache already, it's worth it for performance reasons anyway, just don't treat it as a security control.
The takeaway
wp2shell is a good reminder that "core is safer than plugins" isn't a guarantee, it's a probability. Two individually modest bugs (a missed type check, an off-by-one array desync) combined into full unauthenticated RCE on a plain, unmodified WordPress install. The fix is out, but the proof-of-concept is public too, which means the race between "who patches" and "who scans for it" is already underway. Update today if you haven't.
Sources
Stay Sharp. Weekly Insights.
New posts, framework updates and weekly software conversations.
No spam. Unsubscribe anytime.
Walt is a software engineer, startup founder and previous mentor for a coding bootcamp. He has been creating software for the past 20+ years.