Blog wp2shell: the complete guide to the WordPress core RCE
wp2shell: the complete guide to the WordPress core RCE
wp2shell is a pre-authentication remote code execution chain in WordPress core (CVE-2026-63030 and CVE-2026-60137). What it is, whether you are affected, how to tell if your site was already hacked, and how to clean it.
wp2shell is the most serious WordPress vulnerability of 2026. It lives in core, not in a plugin or a theme. It needs no login, no user interaction, and no unusual configuration. An anonymous attacker sends a few crafted HTTP requests to a default install and ends up with an admin account and a shell.
WordPress patched it in July. Most sites auto-updated within days. That is the good news and also the trap: a patch stops the next attacker, but it does nothing about the one who already got in. Sites that were exposed for even a few hours before updating can still be carrying a webshell today, and the patch left it exactly where it was.
This guide covers everything a site owner needs. What wp2shell is, how the chain works, which versions were vulnerable, how to check whether you were exposed, how to tell if you were already compromised, and how to clean a site that was. If you only read one section, read Am I affected, and what do I do right now.
The short version
- What: a pre-auth remote code execution chain in WordPress core, named
wp2shell. Two bugs chained:CVE-2026-63030(REST API batch route confusion) andCVE-2026-60137(SQL injection inWP_Query). - Who is affected: default WordPress installs on 6.9.0–6.9.4 and 7.0.0–7.0.1 for the full RCE chain. The SQL injection alone reaches back to 6.8.0–6.8.5. No plugin required.
- Fixed in: 6.9.5, 7.0.2, and a 6.8.6 backport for the older branch. Disclosed 17 July 2026 by Searchlight Cyber.
- In the wild: yes, within hours of disclosure. Attackers drop PHP webshells and create rogue admin accounts.
- What to do: update WordPress now, then assume nothing. If you ran a vulnerable version at any point after mid-July, check for a compromise before you trust the site again.
What “wp2shell” means
The name reads as “WordPress to shell.” It is not a piece of malware and it is not a plugin. It is the label the researchers gave to a specific way of turning an anonymous HTTP request into code execution on a WordPress server.
You will see the name attached to three different things:
- The vulnerability chain in WordPress core. This is the real subject.
- A public “am I vulnerable” checker the discoverer put online.
- A cluster of tools on GitHub. Some are defensive scanners that look for the flaw or clean up after it. Others are working exploit code that automates the whole attack. The offensive ones are why mass scanning spiked so fast.
When someone says “we got hit by wp2shell,” they mean an attacker used this core RCE chain to break into their site.
How the attack works
Two vulnerabilities on their own would be serious. Chained, they remove every barrier between an anonymous visitor and full control of the site.
CVE-2026-63030: the front door
WordPress core ships a REST API batch endpoint at /wp-json/batch/v1 (also
reachable as /?rest_route=/batch/v1). It lets a client send several API
requests in one HTTP call. Internally it validates the requests and then
dispatches them, holding the requests and their handlers in parallel lists.
The bug is a route confusion. When one sub-request errors, or when an attacker uses an odd path prefix such as a triple slash, the two lists fall out of alignment. A later request gets executed under the handler that belonged to a different one. Validation ran against the safe request; execution ran against the dangerous one. An unauthenticated call reaches a handler it should never have been allowed to touch.
That is the entry point. It is what lets an anonymous request act as if it passed authentication.
CVE-2026-60137: the injection
Once a request can reach the internal posts machinery without authentication,
the second bug takes over. The author__not_in parameter of WP_Query reaches
raw SQL without proper sanitisation when it is passed as a single value instead
of an array. That is a classic SQL injection: the attacker can read from and,
downstream, influence the database.
On its own, the SQL injection is still dangerous. It is why versions all the way back to 6.8.0 got a patch. But the batch bug is what makes it usable without any credentials.
From injection to admin to shell
The published analyses describe a chain of roughly seven steps. In plain terms:
- The batch route confusion lets an unauthenticated request slip past the auth checks.
- The SQL injection extracts data and lets the attacker plant crafted rows.
- Those rows get loaded back as trusted WordPress objects.
- A cache-poisoning step makes the malicious values persist across requests.
- Nested internal saves run briefly under an administrator context.
- Under that context, a new administrator account is written to the database. No password known to you, no email you recognise.
- That admin uploads a plugin. The plugin is a PHP webshell. From here the attacker runs commands on your server.
The whole thing is a handful of HTTP requests. There is no brute force, no phishing, no waiting. That is why exploitation started within hours.
Which versions are affected
| WordPress version | Full RCE chain | SQL injection only |
|---|---|---|
| 7.0.0 – 7.0.1 | Vulnerable | Vulnerable |
| 6.9.0 – 6.9.4 | Vulnerable | Vulnerable |
| 6.8.0 – 6.8.5 | Not vulnerable | Vulnerable |
| 6.9.5 / 7.0.2 / 6.8.6 and later | Patched | Patched |
“Full RCE chain” means an anonymous attacker can take over the site. “SQL injection only” means the batch route confusion is not present, so the attacker cannot chain to code execution through this path, but the database injection can still leak data. Both got fixed.
How to check your version
- In
wp-admin, look at the bottom right of any screen. The WordPress version is printed there. Also visit Dashboard → Updates. - Over the web, WordPress exposes its version in the page source. View source on
your home page and search for
meta name="generator", or open/wp-includes/version.phpif you have file access and read$wp_version. - With WP-CLI:
wp core version.
If you are on 6.9.5, 7.0.2, or anything newer, the hole is closed. If you were ever on a vulnerable version after mid-July 2026, keep reading. Being patched now does not tell you whether you were clean then.
The timeline, and why “I already updated” is not the end of it
- 17 July 2026: Searchlight Cyber disclosed the chain. WordPress shipped 6.9.5 and 7.0.2 the same day and pushed forced auto-updates to sites that had automatic updates enabled.
- Within hours: the first exploitation attempts hit. Public proof-of-concept code appeared over the following weekend, and mass scanning followed it.
- By late July: monitoring across a large sample of sites showed most had patched, but a meaningful minority had not, and attackers were still walking the internet looking for them.
Here is the part that catches people. Auto-update closed the door. It did not search the house. If an attacker reached your site during the window between the flaw going public and your site updating, the webshell they dropped is still on disk, and the rogue admin they created is still in your database. The update does not remove either. This is the single most common mistake with wp2shell: treating “WordPress updated itself” as “I am safe.”
What attackers do once they are in
Reports from multiple security teams describe a consistent set of post-exploitation behaviours. Knowing them tells you what to look for.
Webshells. These range from a one-line PHP file that runs whatever you POST
to it, up to a full obfuscated toolkit disguised as a plugin, with a file
manager, a database browser, and network scanning built in. Some shells are
dropped into wp-content/cache/ with a random filename and return a fake “404
Not Found” page unless you supply the right secret in the URL, so a casual visit
looks like a dead link.
A fake plugin. The classic move is a new directory under
wp-content/plugins/ that you never installed, often named to look plausible
with a short random suffix, and carrying a fake plugin header that reads
Author: WordPress.org Community so it blends into the plugin list. Inside is
the shell, frequently gated behind a ?c=<command> URL parameter so only the
attacker can drive it. Some campaigns stage it through wp-content/upgrade/
before moving it into wp-content/plugins/, the same path a normal plugin
install uses.
A rogue administrator. A new admin user you did not create. Observed
accounts have used usernames beginning w2s_ followed by random hex, but treat
any admin you do not recognise as suspect, whatever it is called. Attackers use
this account as a second way back in if you find and delete the shell.
Injected JavaScript and redirects. Once they own the site, attackers monetise it. Visitors get redirected to scam pages, fake login screens, or browser-based malware. Sometimes the injection only fires for visitors coming from search engines, or only on mobile, so you as the logged-in owner never see it.
SEO spam. Hidden pages and links get injected to boost other sites in search results. This is what usually triggers Google’s “this site may be hacked” label.
Quiet persistence. Scheduled tasks, extra shells in unrelated folders, and modified core files that re-create the backdoor after you clean the obvious one. This is why a half-cleanup fails.
Am I affected, and what do I do right now
Work through this in order.
- Update WordPress immediately to the latest release, even if you think you
are fine, and then confirm the update actually applied. Do not assume the
forced auto-update did its job. It fails silently on some hosts, usually on
file permissions, and there are documented cases of a site whose update never
installed while the exploit created an admin account on it anyway. Check the
version in Dashboard → Updates after updating. If you cannot reach
wp-admin, update over WP-CLI or ask your host. - Was your site on a vulnerable version (6.8.0 through 7.0.1) at any point after 17 July 2026? If no, and you have automatic updates on, your exposure window was likely minutes and your risk is low. If yes, or you are not sure, treat the site as possibly compromised and run the checks in the next section.
- Look for the obvious signs of compromise (below). If you find any, go straight to If your site was compromised.
- If everything is clean, rotate your security keys and passwords anyway as a precaution, and move on.
Being methodical here saves you from both extremes: panicking over a site that was never touched, and trusting a site that is quietly serving malware.
How to tell if you were already hacked
No single check is conclusive. Run several. The webshell is designed to hide, so you are looking for a pattern, not one smoking gun.
Check your administrator accounts
Go to Users → All Users, filter by Administrator, and read the list. Any account you do not recognise is a red flag. Attackers pick plausible names. Check the registration dates too: an admin created in the exposure window that you have no memory of is a compromise until proven otherwise.
With WP-CLI:
wp user list --role=administrator --fields=ID,user_login,user_email,user_registered
Look for files that should not exist
- List everything under
wp-content/plugins/and match it against the plugins you actually installed. An unfamiliar directory is suspect. - Look in
wp-content/cache/,wp-content/uploads/, and the WordPress root for loose.phpfiles with random names. Uploads should almost never contain executable PHP. - Sort files by modification time. A cluster of PHP files changed within a few minutes of each other, around your exposure window, is a strong signal.
Over SSH:
find wp-content/uploads -name '*.php' -type f
find . -name '*.php' -newermt '2026-07-17' -printf '%TY-%Tm-%Td %TH:%TM %p\n' | sort
Read your access logs
Search your web server access logs for the batch endpoint and the known attack markers:
- Requests to
/wp-json/batch/v1or/?rest_route=/batch/v1, especially ones the server answered with HTTP 207 (Multi-Status). - User-agent strings containing
wp2shellorrezwp2shell. - Bursts of POST requests to the same odd
.phpfile underwp-content/.
grep -E 'batch/v1|rest_route=/batch' access.log
grep -Ei 'wp2shell|rezwp2shell' access.log
A hit does not always mean success, plenty of the scanning was just probing. But a 207 on the batch endpoint followed by new admin activity is the pattern that matters.
Look at what visitors see
- Open your site in a private browser window, and again from a phone, and once by clicking through from a Google search result. Some injections only fire for one of those.
- Search Google for
site:yourdomain.comand look for pages you never wrote, often in languages you do not publish in. - Check Google Search Console for the “Security Issues” report and for a spike in indexed pages.
Check the database for tampering
WordPress core stores things attackers like to abuse in the database, and a file scan will not see them. Look for:
- Unexpected rows in
wp_usersandwp_usermeta(the rogue admin again). - Odd entries in
wp_options, especially anything that looks like base64 or encoded PHP. - Injected
<script>tags or hidden links insidewp_postscontent.
If you are comfortable with the database, dump it and grep for eval(,
base64_decode(, and <script. If you are not, this is a good moment to bring
in help or a tool that can, because leaving a database backdoor in place is the
most common reason a site gets reinfected.
If your site was compromised
The goal is to remove every foothold at once. A partial cleanup leaves a backdoor that rebuilds the rest overnight. Work through all of these, do not stop at the first shell you find.
- Take a snapshot before you touch anything. Copy the current files and database somewhere offline. You want evidence of what happened and a way back if a cleanup step breaks the site.
- Update WordPress core if you have not already. There is no point cleaning a site that is still exploitable.
- Remove the webshells and fake plugins. Delete the unfamiliar plugin
directories and the random
.phpfiles you found. Restore any modified core, plugin, or theme file to its clean original rather than editing out the bad lines by hand, because you will miss some. - Delete the rogue admin accounts and audit the remaining ones.
- Rotate every secret. Change all admin passwords. Generate new
authentication keys and salts in
wp-config.php(this logs everyone out, including the attacker’s stolen sessions). Change the database password and updatewp-config.phpto match. If the site had any API keys or SMTP credentials in the database or config, rotate those too. - Clean the database. Remove injected content from posts and options, and the malicious users and usermeta. This is the step people skip and then pay for.
- Check scheduled tasks and drop-ins. Look at WP-Cron
(
wp cron event list), anymu-plugins, andwp-config.phpfor injectedauto_prepend_filedirectives or extraincludelines. - Reset the ground you cannot verify. Reinstall WordPress core from a fresh copy, and reinstall plugins and themes from their official sources rather than trusting the files on disk. Your content lives in the database and in uploads, so this is safe.
- Ask your host to rescan. If they suspended you or flagged malware, they have their own scanner. Getting them to confirm the site is clean is how you get the suspension lifted. Remember that the code ran with your web server’s privileges, so the compromise can reach beyond WordPress into the rest of your hosting account. If you find files you cannot modify or delete, or signs of activity outside your WordPress folder, treat it as an account-level problem and get your host involved.
- Request a review from Google once the site is clean, through Search Console, to clear the “this site may be hacked” warning. That warning does not lift on its own.
Why it keeps coming back
If a cleaned site gets reinfected within hours or days, one of these is almost always true:
- You removed a shell but left a second one in an unrelated folder.
- You left the rogue admin account, and it just logged back in.
- You cleaned the files but left an encoded backdoor in
wp_optionsor amu-pluginthat rewrites the shell on the next page load. - You never rotated the salts, so the attacker’s stolen session cookie still works.
- The site was never actually patched, and a fresh attacker walked in through the same hole.
Reinfection is not bad luck. It means the cleanup was incomplete. Go back through the list and account for every item.
Do I need to reinstall everything or restore a backup?
Restoring a backup from before your exposure window is the cleanest fix, if you have one and you can afford to lose the content created since. After you restore, update WordPress immediately, because the backup is running the vulnerable version that got you hacked in the first place.
If you do not have a clean backup, you do not need to rebuild from zero. Your
content is in the database and in wp-content/uploads. The executable code,
core, plugins, and themes, is what you replace with known-good copies. Clean the
database, reinstall the code, rotate the secrets. That gives you a site you can
trust without starting over.
What about my passwords, salts, and secret keys?
Assume the attacker read your database, which means they may hold password
hashes and your authentication keys. Change all administrator and user
passwords. Replace the eight keys and salts in wp-config.php with fresh values
from the official WordPress secret-key generator. Changing the salts invalidates
every existing login session, which is the point: it kicks out any session the
attacker is holding. If your database password was ever readable through the
injection, change that as well.
Was my data stolen, and do I have to tell anyone?
A full compromise means the attacker could read the database. If your site stores personal data of customers or members, treat this as a potential data breach and follow your obligations. Under GDPR, a breach of personal data can carry a 72-hour notification duty to your supervisory authority, and sometimes a duty to notify the people affected. This is a decision for you and your legal advice, not something a plugin decides for you. Do not assume “it was just a small site” removes the obligation.
For visitors: while the site was compromised it may have served them redirects, scams, or malware. There is no clean way to notify past visitors, but getting the site clean and out of Google’s warning list stops new ones from being exposed.
I cannot update right away. What holds the line?
Updating is the only real fix. If you are genuinely stuck for a short window, these reduce exposure but do not remove the vulnerability:
- Block the batch endpoint at your edge. If you have a web application
firewall or reverse proxy in front of WordPress, deny anonymous requests to
/wp-json/batch/v1and/?rest_route=/batch/v1. - Restrict anonymous REST API access with a plugin or server rule, so the batch route is not reachable without authentication.
- Watch your logs for the markers above and be ready to pull the site offline if you see a 207 on the batch endpoint.
- A persistent object cache (Redis or Memcached) reportedly closes the route-confusion path, because it changes how the batch requests are handled. Treat this as a bit of extra hardening if you already run one, not as a reason to delay the patch.
Treat all of these as a bandage for hours, not a plan for weeks. Patch as soon as you can.
When the emergency fix breaks your site
This one surprised a lot of owners, and it is worth knowing because it looks exactly like a new problem when it is really the cure.
To buy time, many hosts rolled out a blanket firewall rule that blocks the
/wp-json/batch/v1 endpoint for everyone. That endpoint is not only used by
attackers. The WordPress block editor and several plugins send legitimate batch
requests through it. When the rule is too broad, those requests get a 403, and
the symptom the owner sees is one of these:
- “The response is not a valid JSON response” when saving a post or a settings page.
- A 403 error in the Site Health check, or when a plugin tries to talk to the REST API.
- The block editor failing to save.
If this started right around the wp2shell news and your site is otherwise fine, it is almost certainly the emergency WAF rule, not a hack. Now that core is patched, the blanket block is no longer needed. Ask your host to narrow or remove the rule so legitimate batch requests work again. If you run the rule yourself, scope it to anonymous requests rather than blocking the endpoint outright. Do not disable your own updates to make the error go away, which is the reflex that leaves a site unpatched.
Will a security plugin catch or clean this?
Be clear-eyed about what a plugin can and cannot do here, because the honest answer is different for prevention and for cleanup.
Prevention. wp2shell is exploited by an unauthenticated request that reaches WordPress before most plugin code runs. A plugin-based firewall filters requests that PHP has already received, so it can help, but it is not a substitute for the patch or for an edge firewall in front of the server. The reliable prevention is updating core. Anyone who tells you a plugin alone would have stopped a pre-auth core RCE at the door is overselling.
Detection and cleanup. This is where a scanner earns its place, and it is where Segurium is built to help. The webshell and the fake plugin are files on disk, so a file scanner finds them. An integrity scan compares your core, plugin, and theme files against verified upstream copies and flags anything that was modified or does not belong, which is exactly how a tampered core file or a planted fake plugin shows up. Segurium runs that check through its Cloud Threat Inspection service, and when it finds injected code inside a real file it removes the injected part and writes the file back working, rather than deleting it and taking your site down with it. A real-time scan checks new and changed files on a schedule, so a shell dropped after your last full scan does not sit unnoticed.
Two limits worth stating plainly. A file scanner does not read your database, so
the rogue admin account and any encoded backdoor in wp_options are yours to
find and remove with the steps above. And a plugin does not watch Google’s
blocklist for you, so clearing a “this site may be hacked” warning is a manual
request through Search Console. Knowing where the tool stops is how you avoid a
half-cleanup.
Frequently asked questions
- Is wp2shell a virus or a plugin I can uninstall?
- No. It is a vulnerability in WordPress itself. You do not uninstall it, you patch WordPress. The webshell an attacker leaves behind is a separate file you remove.
- I run the latest WordPress. Am I safe?
- Safe from future attacks through this bug, yes. But if you ran a vulnerable version after mid-July 2026, update alone does not tell you whether you were already broken into. Run the compromise checks.
- Why would anyone bother attacking my small site?
- They are not picking you out. The attacks are automated and run against the whole internet at once. A small, low-traffic site is just as useful to them for sending spam, hiding a command server, mining, or boosting other sites in search results. Being small is not protection.
- Do I actually need a plugin installed to be vulnerable?
- No. This is a bug in WordPress core. A clean, default install with no plugins at all was exploitable on the affected versions. That is what makes it unusual.
- Am I safe because I run an old version like 6.8?
- Partly, and it is a trap. The 6.8 branch does not have the batch route confusion, so the full takeover chain does not reach it. But 6.8.0 through 6.8.5 still carry the SQL injection and were patched in 6.8.6. Update anyway. An unsupported old version has plenty of other holes.
- My update caused a "not valid JSON" error or a 403. Did I get hacked?
- Probably not. Many hosts blocked the batch/v1 endpoint as an emergency measure, and that block also breaks the block editor and some plugins that use the endpoint legitimately. See the section above on when the emergency fix breaks your site. Ask your host to relax the rule now that core is patched.
- How do I check which version I am on?
- Bottom right of any wp-admin screen, or Dashboard then Updates, or run wp core version on the command line.
- My host suspended my account or emailed me about malware. What now?
- Your host's scanner found something. Do not ignore it and do not just ask them to unsuspend. Work through the cleanup, then ask them to rescan and confirm the site is clean, which is what gets the suspension lifted.
- Where does the webshell hide?
- Common spots are a fake plugin folder under wp-content/plugins/, random .php files in wp-content/cache/ or wp-content/uploads/, and modified core files. Some shells return a fake 404 unless you know their secret query string, so they look like dead links.
- I cleaned it and it came back. Why?
- The cleanup missed something: a second shell, the rogue admin account, an encoded backdoor in the database, or unrotated salts. See the section above on why it keeps coming back.
- A scan came back clean. Am I definitely safe?
- A clean scan lowers the odds but does not prove it. A capable attacker removes obvious traces, and a file scanner does not read your database. Pair the scan with the manual checks above: your admin user list, your logs if you have them, and the database. Agreement across several checks is what should reassure you.
- I am on shared hosting and have no access logs. How can I even tell?
- Lean on the checks that do not need logs. Read your administrator list for accounts you did not create. List your plugins and compare against what you installed. Look for stray PHP files in wp-content/uploads and wp-content/cache. Check the database for unexpected users and encoded content. If your host keeps logs on its side, ask them for the relevant window.
- My host will not investigate and I cannot change the firewall rule. What now?
- Do the parts you control: update, remove what you find, rotate every secret, reinstall the code, clean the database. For anything at the account or server level, keep pushing your host with specifics, the exact files, the dates, the log lines. If they still will not act and the site holds real data, that is a reason to move to a host that will.
- Do I have to reinstall WordPress from scratch?
- Not from zero. Replace the code (core, plugins, themes) with clean copies, clean the database, rotate secrets. Your content and uploads are preserved.
- Should I restore a backup?
- If you have one from before the exposure and can accept losing recent content, yes, then update immediately. If not, clean in place.
- What passwords and keys do I change?
- All user passwords, the eight keys and salts in wp-config.php, and the database password if it could have been read. New salts log everyone out, including the attacker.
- Was my data stolen?
- Assume the attacker could read the database. If you hold personal data, treat it as a possible breach and follow your notification duties.
- Does a firewall protect me if I cannot update yet?
- An edge firewall that blocks the batch endpoint reduces exposure. A firewall that runs inside WordPress helps less against a pre-auth core bug. Neither replaces the patch.
- Will Wordfence, Sucuri, or another scanner have caught this?
- A scanner can detect the webshell and the file changes after the fact and help you clean them. No scanner reliably blocks a pre-auth core RCE at the door before WordPress runs. Patching is the prevention.
- My site shows "this site may be hacked" in Google. How do I remove it?
- Clean the site fully, then request a review in Google Search Console under Security Issues. The warning does not clear by itself.
- Is there public exploit code?
- Yes. Working exploit frameworks are on GitHub, which is why scanning and exploitation scaled so quickly. That raises the urgency of patching and checking, it does not change the fix.
- How was this even found?
- Searchlight Cyber disclosed it on 17 July 2026. WordPress credits the SQL injection to the researchers TF1T, dtro, and haongo, and the batch route confusion that turns it into an RCE to Adam Kues. The details are in the advisory, linked below.
Sources and further reading
- Searchlight Cyber, original advisory
- Wiz, exploitation in the wild
- BleepingComputer, webshells installed via wp2shell
- Malwarebytes, what happens if you visit a hacked site
- Elastic Security Labs, end-to-end detection
- The Hacker News, exploitation grows as public exploit fuels scanning
- NVD, CVE-2026-63030
- NVD, CVE-2026-60137
If your site was hit and you want the cleanup done without wiping it, that is what Segurium is for. Update WordPress first, then run a scan.
Tags: #wordpress , #cve , #exploit , #malware , #cleanup