WP-SHELLSTORM exploited four systemic weaknesses that every mass campaign reuses: open XML-RPC, missing MFA, over-provisioned users, and no auto-update policy. This article maps each TTP to a preventive control you can deploy today. The next campaign will be faster. Your defense needs to be faster still.
WP-SHELLSTORM compromised over 25,000 WordPress sites in its first 72 hours. Not through zero-days in core. Not through nation-state level sophistication. Through the same four doors that every mass-targeting campaign has used since 2018. The attackers did not break WordPress. They just walked through doors that were never locked.
If you read the post-mortems, you saw the IoC lists. The file paths. The PHP function calls. Useful for cleanup. Useless for prevention. Because the next campaign will ship different payloads, different obfuscation, and different staging servers. But it will target the same four doors.
Here is what you actually need to lock, and the exact commands to lock them.
Door 1: XML-RPC Is Still Open on Most WordPress Sites
WP-SHELLSTORM used system.multicall in XML-RPC to amplify credential-stuffing attacks. One HTTP request could test 100 password combinations. The server processed all of them before returning a single error. This is not new. It has been documented since 2015. Yet more than 60% of WordPress sites still have xmlrpc.php responding to anonymous requests.
Most site owners keep XML-RPC active because they are afraid of breaking the WordPress mobile app or Jetpack. Here is the truth: Jetpack does not need XML-RPC anymore. It switched to REST API years ago. The WordPress mobile app also uses REST API by default since version 18. If you are not running a specific client that still requires XML-RPC, you are carrying dead weight that attackers love.
Verify it yourself: Run this from any terminal.curl -X POST https://yoursite.com/xmlrpc.php -d '<?xml version="1.0"?><methodCall><methodName>system.listMethods</methodName></methodCall>'
If you get back an XML response with method names, your server is leaking attack surface to the entire internet. Block it at the web server level, not with a plugin.
Nginx Block
location = /xmlrpc.php {
deny all;
access_log off;
log_not_found off;
}
Apache .htaccess
<Files "xmlrpc.php">
Require all denied
</Files>
If you absolutely need XML-RPC for a specific integration, do not leave it open to the world. Restrict it to the originating IP address of the service that needs it. That reduces the attack surface from the whole internet to one IP.

Door 2: No MFA Means One Stolen Password Is Game Over
The WP-SHELLSTORM campaign did not use zero-day exploits to breach admin accounts. It used credential-stuffing lists from previous database leaks. The same passwords that appeared in the 2021 LinkedIn leak, the 2023 Lastpass breach, and dozens of other dumps were being tried against WordPress admin panels. And they worked.
Multi-factor authentication is the single most effective control against credential-based attacks. CISA, NIST, and every security framework on the planet agrees. Yet most WordPress sites still rely on a password as the sole gate between an attacker and full administrative control.
Here is the counter-intuitive part: traditional 2FA (TOTP codes, SMS) is not enough for admin accounts. If an attacker has already stolen the password through a phishing page, they can intercept the 2FA code in real time through a reverse proxy. What you need is phishing-resistant MFA. WebAuthn passkeys stored on hardware security keys or device biometrics. The private key never leaves your hardware. No phishing page can steal what is not transmitted.
For a full implementation guide, check out our phishing-resistant authentication walkthrough for WordPress. The key shift is moving from “something you know” (password) to “something you have” (hardware key) as the primary factor, not just a second step bolted on after the password.

Door 3: Everyone Is Still Running as Administrator
During the WP-SHELLSTORM incident, forensic analysis showed that many compromised sites had five, six, or more active administrator accounts. Some of those accounts had not logged in for two years. Some belonged to former employees who still had full site access. Every unused admin account is a liability waiting to be exploited.
The principle of least privilege is not a theoretical concept from security textbooks. It is a practical control that directly limits blast radius. If a content editor account gets compromised, the attacker can edit posts and upload media. They cannot install plugins, edit themes, create new users, or modify site configuration. The blast radius is contained.
Three actions to take today:- Audit every user account on your WordPress install. Remove any account tied to a former employee or contractor who no longer needs access.
- Rebuild your role map. No one should have Administrator capabilities unless they are responsible for site configuration, plugin management, and user administration. Content creators should be Editors or Authors.
- Disable the file editor in wp-config.php:
define('DISALLOW_FILE_EDIT', true);. This prevents any authenticated user from editing PHP files through the WordPress dashboard, even if they are an administrator.
For a deeper role audit process, read our guide on auditing WordPress user roles. And if you manage multiple sites, implement a centralized identity provider like Azure AD or Google Workspace to enforce least privilege at scale rather than managing roles manually on each install.

Door 4: No Auto-Update Policy Means the Patch Gap Kills You
The average time between a CVE publication and mass exploitation is now under 15 hours. For critical-severity vulnerabilities, that window drops to under 4 hours. If you are manually reviewing and deploying WordPress updates, you are losing the race. WP-SHELLSTORM exploited plugins that had patches available for weeks. The sites that were compromised had simply not applied them.
An auto-update policy is not optional. It is the difference between being patched before the exploit drops and being part of the incident report. WordPress has supported automatic updates for core, plugins, and themes since version 5.5. Enable them. But do not just flip the switch blindly. Use a staged rollout.
Staged Auto-Update Policy
- Stage 1: Enable auto-updates for security patches only on staging instances. Monitor for 48 hours.
- Stage 2: Enable auto-updates for all plugins and themes on staging. Run your integration test suite.
- Stage 3: Enable auto-updates for security patches on production. Minor version bumps only.
- Stage 4: Enable major-version auto-updates on production after verifying compatibility in stage 2.
Use the auto_update_$type filter in your theme's functions.php or a must-use plugin to control which components update automatically. Here is a pattern that enables security auto-updates for plugins while blocking major version bumps until you approve them:
add_filter('auto_update_plugin', function($update, $item) {
$major = explode('.', $item->new_version)[0];
$current_major = explode('.', $item->version)[0];
if ($major !== $current_major) {
return false; // Block major version bumps
}
return $update;
}, 10, 2);
The key insight is that auto-update is not about surrendering control. It is about shifting your control from manual deployment to policy-based automation. You still decide what gets updated and when. But the execution is automatic and immediate.

The Framework: Lock, Verify, Monitor
These four controls XML-RPC disable, MFA enforcement, least-privilege access, and auto-update policy form the foundation of a campaign-ready WordPress hardening posture. But controls are only effective if they are verified. Add these three verification steps to your monthly maintenance routine:
- Lock verification: Run the curl command against xmlrpc.php monthly. Confirm it returns 403.
- MFA verification: Have a team member attempt to log in without their hardware key. Confirm they are blocked at the WebAuthn challenge.
- Update verification: Check the WordPress Site Health screen for any components that have pending updates older than 7 days. If you find any, your auto-update policy has a gap.
The next mass-targeting campaign is coming. It might use different payloads than WP-SHELLSTORM. It might target different plugin vulnerabilities. But it will try the same four doors. Lock them now, and the next incident report will not include your site.
For deeper mitigation techniques, read our WAF rules playbook for WP-SHELLSTORM and the defense-in-depth framework for WordPress. These two guides complement this hardening checklist with detection-layer and response-layer controls.
Frequently Asked Questions
Does disabling XML-RPC break any WordPress features in 2026?
Very few. The official WordPress mobile app, Jetpack, and most remote publishing tools have migrated to the REST API. The only edge case is legacy apps like Windows Live Writer or some custom integrations built before 2019. If you are unsure, run XML-RPC in logging mode for a week and check the access log for legitimate calls before disabling it.
What if my host does not support WebAuthn for MFA?
WebAuthn is a browser-level standard. It does not require any host-side support beyond your WordPress server running PHP 7.4 or later with the gmp or bcmath extension. Any modern WordPress hosting environment supports it. If your host blocks the necessary headers, switch to a TOTP-based authenticator app as a bridge step while you push for WebAuthn adoption.
How often should I audit WordPress user roles?
Quarterly for most organizations. Monthly if you manage more than 10 admin-level users or have high employee turnover. Every audit should export the user list with roles and last-login timestamps. Any account inactive for 90 days with elevated privileges should be demoted to a lower role or removed.
Will auto-updates break my site if a plugin update introduces a bug?
That is why staged rollout matters. Run updates on a staging environment first. Use a must-use plugin to delay auto-updates by 48 hours for non-security patches. If a critical security patch needs immediate deployment, you can override the delay manually. The risk of running unpatched software far exceeds the risk of a plugin update bug, which can be fixed with a single rollback command.
What is the single most impactful hardening step for a single WordPress site?
Enable auto-updates and enforce phishing-resistant MFA on all administrator accounts. These two controls alone eliminate the two most common entry vectors in every mass-targeting campaign: unpatched software and stolen credentials. Everything else is additive. Start there, then layer on XML-RPC disable and least-privilege access.



