Sorting out WordPress websites compromised by wp2shell.com

Webarchitects provides hosting for a lot of WordPress sites, which clients manage themselves, many of these were hit badly by the wp2shell vulnerability on 19th July 2026, following are some notes on what we have been doing to detect and sort out compromised sites.

I’d be interested in hearing what others are / have been doing?

Using WP-CLI to detect compromised sites

The nightly version of the WordPress command line interface, WP-CLI, (note that there hasn’t been a release for ages, version 3.0.0 will hopefully be released soon, the last release is too old to be used) can be used to detect compromises using:

wp core verify-checksums
wp plugin verify-checksums --all
wp user list

We have an Ansible role to install WP-CLI:

Restoring from a compromised site

Ideally a backup from prior to 19th of July should be used for restoring a compromised site, however this might not be available, in that case a fresh WP install and the following steps can be followed.

Additional WP administrators

The users list from WP-CLI might not be complete, @liam found a compromised sites that omitted additional administrators from the output so best open a MariaDB / MySQL dump and search for the wp_users table, if it looks a bit like this:

LOCK TABLES `wp_users` WRITE;
/*!40000 ALTER TABLE `wp_users` DISABLE KEYS */;
INSERT INTO `wp_users` VALUES

...

(12,'wpsvc_24a94efb28f9','$wp$2y$10$rTb5pE6VwTk8paDZFN7r.OUR0C5dnRMl5OXwcDX2oB.Engoc3uXRi','wpsvc_24a94efb28f9','wpsvc_24a94efb28f9@wordpress-svc.internal','','2026-07-19 03:58:39','',0,'wpsvc_24a94efb28f9'),
(13,'wpsvc_782a3557d321','$wp$2y$10$ZDAltpECRVjj1RotLrKXOeAFdN5v7InPW12DZQ7m98DTADOtHCcQu','wpsvc_782a3557d321','wpsvc_782a3557d321@wordpress-svc.internal','','2026-07-19 05:34:43','',0,'wpsvc_782a3557d321'),
(14,'wp2_8cb6333de79a','$wp$2y$10$xxZXx3B3BAvtv.HktTqvPuV7maJQgnmU1vI0/RUuXwmnx3D5D6UMC','wp2_8cb6333de79a','wp2_8cb6333de79a@wp2shell.invalid','','2026-07-20 05:50:17','',0,'wp2_8cb6333de79a'),
(15,'wp2_ba36970ef717','$wp$2y$10$QvzJBACtXWmw9lAiW9IcjutMPPVrcjHyfqrd3entq5xmBk9uIKvdi','wp2_ba36970ef717','wp2_ba36970ef717@wp2shell.invalid','','2026-07-20 05:52:10','',0,'wp2_ba36970ef717'),
(16,'wpenginebot','$wp$2y$10$T64YfK0/9wgXA2AhdDV9k.pVSzQ/E3dApg5qeUfdz0eGP5/z2Vdf.','wpenginebot','wpenginebot@wpengine.com','','2026-07-20 13:51:19','',0,'wpenginebot'),
(17,'w2s_73e4d42cae40','$wp$2y$10$Pyc9/ctRa8ZVV4QnbWHoHeu.gNs4U0CtTAYtbQlhxMfDQSL3LIHP2','w2s_73e4d42cae40','w2s_73e4d42cae40@shellcode.lol','','2026-07-20 19:04:30','',0,'w2s_73e4d42cae40');
/*!40000 ALTER TABLE `wp_users` ENABLE KEYS */;
UNLOCK TABLES;

Then these account need deleting, note that the last INSERT needs to end in a ; not a , prior to restoring from a dump like this.

Uploads

The only directories and files that generally need copying are the wp-content/uploads/2* ones and these should only be image and PDF etc files, the web server should be set to dissallow PHP from running from wp-content/uploads, eg:

    <Directory "/home/example/public_html/wp-content/uploads">
      RemoveHandler .php
      <FilesMatch "\.php$">
        <If "-f %{REQUEST_FILENAME}">
          Require all denied
        </If>
      </FilesMatch>
    </Directory>

Plugins

Install them all from upstream, not from the compromised site / account:

wp plugin install akismet

However note that any added on or after 19th July 2026 should not be installed! For example:

ls -lah /home/example/public_html/wp-content/plugins
drwxr-xr-x 26 example example 4.0K Aug 24 23:36 .
drwxr-xr-x 10 example example 4.0K Aug 25 02:32 ..
drwxr-xr-x  2 example example 4.0K Jul 19 03:59 content-delivery-helper-75658a
drwxr-xr-x  2 example example 4.0K Jul 20 08:51 mm
drwxr-xr-x  2 example example 4.0K Jul 19 05:34 security-headers-manager-b28343

Any paid for or custom ones can’t be installed like this.

Themes

Best install from upstream:

wp theme install twentytwentyfive

However any paid for or custom ones can’t be installed like this.

Updating WP core, plugins and themes daily

We are running this Bash script daily to update WordPress core, plugins and themes and also run the checksum tasks, as the user running the site, with the results being emailed to the owner of the site:

Note that false positives for git checkout plugins and themes can be ignored with the addition of --ignore-vcs argument being used.

Of course there is the danger that an update might break things or a WP plugin might be compromised upstream and get installed before it is removed from wordpress.org however in our experience this is currently rare compared to the chance of an out of date site being compromised.

Using Icinga to check WP checksums

We are adding the wordpress-checksums Icinga check for all sites to alert us to issues in the future, this can also be run using a shell:

Commercial scanners

These two WordPress CLI security scanners require you to pay to use their API:

We have an Ansible role to install wordfence and intend to also write one for wpscan when we have time:

Security forensics consultants

We haven’t used any of these organisations but they might be able to help in some circumstances (for example a WP compromise being escalated into a server compromise):

2 Likes