How to Detect & Remove Malware from a WordPress Site

Published 3 September 2024
Updated 31 October 2024
Oliver Sild
CEO at Patchstack
Table of Contents

Performing a WordPress malware removal in a way that you can be sure that it’s clean is not an easy task. That’s why a WordPress malware removal can cost over 150 dollars – and that’s not considering lost revenue, wasted ad spend or long-term negative SEO consequences. Furthermore, depending on the service provider you can’t be sure if the site was even properly cleaned or not.

In this tutorial we will guide you through a comprehensive WordPress malware removal process for removing malware from WordPress websites, fixing the vulnerabilities, and removing sites from the blacklists. I will also include some hands-on suggestions on how to keep the sites protected in the future.

Do you know what malware exactly is? If you don’t check out the video below.

Disclaimer

A lot depends on the hosting environment. For example, if you have another site installed to subfolder or subdomain and they are located in the same hosting account, make sure to isolate them. If you had multiple websites in the same hosting account, keep in mind that you need to go through the same process with every single website in the same hosting environment.

If there are additional sites in your hosting account (can you access all sites with the same FTP account?), make sure to lock them down too before starting the whole malware removal process. Malware can move from one site to another and can infect your whole hosting environment. In this case, it’s often hard to tell which site was originally vulnerable.

This tutorial will get slightly technical. So sit back, get some coffee, and expect to learn some new things.

1. Lock down the site(s) before starting WordPress malware removal

It’s important while doing the WordPress malware removal to be sure that during the clean-up process, nobody else than you have to access to the site. Some hosting environments allow you to put the site into a maintenance mode, if you can’t find such options from your service provider, you can lock down your site like this:

1.1 Apache

Open your .htaccess (sometimes, htaccess.txt) file and write the following lines on top:
PS! Don’t forget to change the allow IP with yours.

order deny,allow
# Deny access from all IPs
deny from all
# Allow access from specific IP
allow from 127.0.0.1

1.2 Nginx

Open your nginx.conf file and write the following lines:

location / {
# allow your IP below
allow 127.0.0.1;
# drop rest of the world
deny all;
}

2. Install anti-virus software on all computers from where the site is accessed

It’s not uncommon that your FTP access, /wp-admin/ username and password, or even your hosting environment credentials are leaked via the keylogger or some other computer virus. Having anti-virus software installed on the computers from where you access the admin panel or log in to your hosting environment or FTP is essential.

For example, scan your PC for possible malware with Malwarebytes. Also, take a look at your operating system security settings and make sure the firewall is turned on.

PS! Frequently update your OS, web browsers, and browser extensions.

3. Change all access codes (Hosting, SSH, FTP, MySQL, WP Users)

After you have closed your site for the public and scanned your PC for malware, make sure your credentials are not leaked by changing them one by one. Change your hosting panel password, then revoke all FTP accounts and create new ones with the pre-generated password (some hosts do it automatically if not, use Keepass2 or some other Password Management tool like Dashlane and LastPass).

3.1 Change Database credentials:

When you’re about to change the MySQL or MariaDB password (or any other database credentials) you also need to update this information on your wp-config.php file.

3.2 Change Salts:

Salts are used to safeguard passwords in storage. Historically a password was stored in plaintext on a system, but over time additional safeguards developed to protect a user’s password against being read from the system. A salt is one of those methods. A new salt is randomly generated for each password, after the breach, it’s important to replace the old salts with new ones in your wp-config.php file.

You can generate new Salts here:
https://api.wordpress.org/secret-key/1.1/salt/

3.3 Change WordPress admin panel access:

Log into your WordPress site, navigate to Users, and delete all inactive accounts. Then click Edit on active accounts one by one and under Account Management suspend all sessions and Generate new passwords for all users.

PS! Make sure you don’t have an account with Admin or Administrator username.

4. Make a full backup of your website

If your hosting provider doesn’t have any backups, make sure to download the whole content of your fileserver and database to a local environment.

4.1 SSH:

Some servers give you SSH access, which can make your life much easier when doing WordPress malware removal. The process of having SSH access on different hosting environments can differ, for example, GoDaddy has options on their hosting panel:
https://uk.godaddy.com/help/enable-ssh-secure-shell-access-4942

When you have successfully logged into the site with SSH access, perform the following:

zip -r backup-pre-cleanup.zip .

This might take some time but will generate a .zip file with all the files on your hosting account. You can later download the .zip file directly over SFTP.

4.2 SFTP:

SFTP is the secure version of the FTP protocol. File Transfer Protocol is used for transferring files from one machine to another. You can get your SFTP access codes from the same place on your hosting account as the regular FTP access codes. Remember that the SFTP port is 22 instead of 21 (FTP port).

You can access the server with SFTP/FTP client like FileZilla. You can then make a local folder to your PC i.e backup-pre-cleanup and drag and drop the whole content of your server into this folder.

PS! This will take more time than compressing the contents into a .zip file first via SSH.

4.3 Database:

In most of the hosting environments, there is phpMyAdmin installed which allows you to manage the database. You can easily export the whole database with an export option via phpMyAdmin panel. Save this to the same folder “backup-pre-cleanup”.

You can also export your database via SSH with the following command:
mysqldump -p -h hostname -u username database > backup-pre-cleanup.sql

Make sure to change the hostname, username, and database with your database credentials (you can find them from wp-config.php).

PS! After exporting it via SSH, make sure to download this to your local environment and delete it from the file server.

5. Analyze logs and recent changes

Logs are always the best place to look after an incident to detect what was changed or what was added to the server. Download access logs, if you can’t find the place then ask them from your hosting provider. Open the logs with software like Sublime and search for the “POST” method. Look at the dates and see if any PHP file has been added to the server and also look at the events around suspicious behavior.

You can also check which PHP files have been edited most recently, for this you can run the following command via SSH:
find . -type f -name '*.php' -ctime -7

You should also see if there are any JavaScript files added or modified:
find . -type f -name '*.js' -ctime -7

-ctime -7 will show you all files modified (or if permissions/attributes are modified) in the last 7 days. You can change the number lower/higher depending on the need while:
-7 = modified in less than 7 days
+7 = modified more than 7 days ago

PS! Make sure to do this before updating or changing a larger amount of files in your WordPress installation.

6. Update your WordPress installation

The most probable cause why your website got infected is the fact that you’re probably having outdated code, plugins or themes installed. Sites loaded with too many plugins or having a default username and weak passwords are the main reasons why WordPress sites get hacked.

6.1 Remove all unused plugins/themes from the installation.

Even if you deactivate the plugin or theme you’re not using, it will stay on the server and can still be exploited if it’s vulnerable under certain conditions.

It’s always better to remove unwanted software to reduce the risk of having outdated/vulnerable software in the system.

7. Update your PHP version

PHP 8+ makes your website run 20% faster (compared to PHP 7), and is also more secure. You should upgrade your site from any old PHP version to the latest.

Old PHP versions will sooner or later be deprecated, thus making your site incompatible with different WordPress-related software.

8. Remove Symbolic Links

A lot of vandals try to symlink folders to gain access to root or other higher folders in your hosting environment. Sometimes, if a symlink goes undetected and you find the linked folder and try to delete it, you might end up deleting all files in your server. Make sure there are no Symlinks before changing file/directory permissions recursively.

To avoid this from happening, use the following command via SSH (or where you see the suspicious folder):
find . -type l -exec unlink {} \;

9. Set proper file permissions

Avoid having any file or directory set to 777. By default, all folder permissions in WordPress should be 750 while all files (except wp-config.php which can be as low as 400) should be 644 or 640.

Via SSH, you can change all folder permissions recursively to 750 with the following command:
find /path/to/your/wordpress/install/ -type d -exec chmod 750 {} \;

Via SSH, you can change all file permissions recursively to 640 with the following command:
find /path/to/your/wordpress/install/ -type f -exec chmod 640 {} \;

Via SSH, you can change wp-config.php permission to 400 with the following command (make sure to test and if needed change it to 440, if this is not working, stick to defaults):
chmod 400 /path/to/your/wordpress/install/wp-config.php

There are possibilities to change them to be more restrictive, but this depends on the hosting environment. You can read more about file permissions from the official WordPress Codex.

10. WordPress malware removal (Files)

This paragraph will be a combination of open-source tools to detect suspicious files automatically and manually. We always recommend doing as much as possible manually to understand your application and to have a peace of mind with the confidence that the WordPress malware removal is done correctly.

10.1 Diff a clean installation with the infected one

Create a new WordPress installation and install exactly the same plugins, themes, and make sure everything runs at the same version. Create a new folder to your local environment named “Compare” and add 2 folders inside the folder “Clean” and “Infected“. Using SFTP, download the freshly made WordPress installation and save this to the “Clean” folder. Now open your previously made backup and find the WordPress installation from there and Copy this to the Infected folder.

Download an application called Beyond Compare and compare the two folders Clean and Infected, keep your main focus on PHP and JavaScript (JS) files which are different from the originals. Open your SFTP access to the original site and open the Clean folder locally. For example, if Beyond Compare is telling you that index.php and wp-mail.php are being different on those two folders, move as many files from the Clean folder to your website and check if the site is working properly after replacing each file. If the site breaks, then just revert it by uploading the same file back to the server from the Infected folder.

This method will allow you to manually restore infected WordPress, Plugin, and Theme files with originals. If you feel comfortable with terminal, you can also use the diff command via SSH like this:
diff -r wordpress-clean/ wordpress-infected/ -x wp-content

PS! Keep in mind that the wp-config.php file will be different on every site. Use this method only for detecting if original files have been changed, or if additional files have been added to the main system folders (/wp-content/uploads/ is not a system folder).

10.2 Remove PHP files from uploads folder

PHP files should never be in the uploads folder, but in many cases when a vulnerable upload functionality is being exploited, the backdoors and droppers end up exactly there.

Open up your SSH terminal and navigate to /wp-content/uploads/ (you can navigate with cd command i.e cd /wp-content/ then cd /uploads/) and run the following command:
find . -name "*.php"

10.3 Find/Remove obfuscated backdoors and malware (manually)

Web-shells (backdoors) and malware are often highly obfuscated and hidden (sometimes also added to the headers of original filesystem scripts) to avoid detection by automatic malware scanners.

Some functions that are very commonly used in backdoors and obfuscated malware are eval()base64_decode()gzinflate()str_rot13()

To locate such files, open your SSH terminal and run the following command:
find . -type f -name '*.php' | xargs egrep -i "(mail|fsockopen|pfsockopen|stream\_socket\_client|exec|system|passthru|eval|base64_decode) *("

Also, look for a backdoored image files:
find wp-content/uploads -type f -iname '*.jpg' | xargs grep -i php

And iframes:
find . -type f -name '*.php'  | grep -i '<iframe'

10.4 Find/Remove obfuscated backdoors and malware (automatically)

There are different tools and scanners available which search for malware patterns automatically. Here I will only list open-source tools that have been working well in the WordPress malware removal process. Keep in mind that most of the malware and backdoors are built to be undetected by such tools, so the manual audit is still always needed.

10.4.1 OWASP web malware scanner

It will scan a web application using a community-driven signature database. It can be used to identify compromised WordPress, Joomla, and other popular web application installations. The Web Malware Scanner will scan for both MD5 hash-based signatures and malware signatures using YARA rules.

Github: https://github.com/redteamcaliber/WebMalwareScanner

10.4.2 Ai-Bolit malware scanner

Works well to detect obfuscated code. Also looks for malicious JS code and has different detection levels. Works as a PHP script that you upload to your website and it will start scanning the site as soon as you navigate to the file. Uses pattern based and heuristic scanning. Finds a lot, sometimes too much (false-positives).

Website: https://github.com/KashifHK123/AI-Bolit

10.4.3 PHP malware scanner

PHP malware scanner searchers for PHP extensions and test files against text or regexp rules. The rules are based on self-gathered samples and publicly available malware/webshells.

Github: https://github.com/scr34m/php-malware-scanner

10.5 Repeat step 9.1 and make sure there are no suspicious files left

Remove files one-by-one which were detected by scanners or which looked suspicious and always check if the site is still working after the file has been removed. Now, after removing all the suspicious files you have detected, download the cleaned-up WordPress folder to the Infected folder and compare it once again with the Clean folder by using the Beyond Compare application.

Keep in mind that file-structure (except /wp-content/) should be identical. Look for cryptic and obvious file names like N73He.php and hax0r.php etc.
Sometimes, vandals add their files to every directory as a message, which might include something like “hacked by Team_CC“.

You can remove such files recursively with the following command via SSH:
find . -type f -name "filename.php" -exec rm -f {} \;

11. WordPress malware removal (Database)

It’s very common that malware is injected into the database and will be loaded to the site via Posts, Pages, Comments and other site content. We already exported the .SQL backup of the WordPress database. There are different ways of how to perform WordPress database malware removal.

11.1 Searching for suspicious content directly from the .SQL database backup

You can open .SQL file directly with Sublime. Use ctrl + f or cmd + f to find malicious content from the database:

Search for iFrames: <iframe
Search for base64: base64_decode
Search for eval(): eval()
Search for scripts: <script

List all the malicious findings and try to understand where are they located. Don’t delete them directly from the database backup, but rather continue to 11.3.

11.2 Searching for suspicious content via phpMyAdmin

If you have access to phpMyAdmin, you can directly search for similar entries via search option. If you’re sure you have detected a malicious content, try to understand from where it was added and continue to 11.3

11.3 Going manually through WordPress Pages, Posts, Comments and Revisions

Look at the pages, posts, and their revisions. Navigate to suspicious entries found from 10.1 and 10.2 and proceed to editor one-by-one and select Text mode. Delete malicious code and if needed, re-format the content. Don’t forget to go over Revisions. Also, look at the comments and Delete possible spam.

12. Check the site manually and from a search engine perspective

Take a look at your site as a visitor, look if you can still see anything suspicious or of the site is performing unexpectedly. Google your website. Add the following query to Google search site:mywebsite.com if you see a bunch of Chinese hieroglyphs or suspicious Canadian pharma offerings in Google results, then you can be sure that the site has been infected with SEO malware. Such malware only shows itself to Google and other search engine crawlers and make it invisible for visitors and site owners to see.

Install User-Agent Switcher extension for Google Chrome, which allows you to see your site from the search engine perspective. You can set a custom user-agent from the extension settings, the most popular user-agent used by Google bot is:
Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)

If you visit the links now with Google bot user-agent and don’t see different content, you successfully removed the SEO Injection/Cloaking malware, if you still see weird content loaded, go back to the malware removal process and see what you missed.

13. Restore public access to the website

If you have double-checked and gone over all the steps and feel confident that the site is now clean, remove the restrictions from your website (only the site that you just cleaned up, if you have other sites in the same hosting environment, don’t open them until you have done the same process with them).

If your hosting provider blocked access instead, ask them to rescan your site, tell them you conducted a WordPress malware removal, and ask them to restore the public access to your website.

14. Ask to be removed from Blacklists

The best way to check if your site has been blacklisted by any AV vendors or search-engines is by using VirusTotal. If you’re blacklisted by Google Safe-browsing, you can just log in to Google Webmaster Tools and request a re-scan. It can take a few days and if Google can’t detect malware anymore, they will remove you from Google Safe-browsing blacklist.

If you are blacklisted by AV vendors or other search engines, navigate to their site and ask to be re-scanned. In most cases, you need to manually fill a form to request a rescan and the process can vary with different blacklists.

If you’re wondering what a blacklist is, then we have an article about this here.

15. WordPress Hardening

There are some things you should do additionally after you have completed WordPress malware removal. There are some simple tweaks you can do easily to prevent some popular infections.

15.1 Disable PHP execution in /uploads/ and /cache/ folders

You can easily add a few lines of code into your Apache or Nginx configuration which will prevent PHP usage inside /upload/ and /cache/ folders. In many scenarios, this can render the initial backdoor or dropper useless as it can’t be executed even if arbitrary file upload was successful.

Nginx:
# Deny access to PHP files in any /uploads/ or /cache/ directories
location ~ /uploads/(.+)\.php$ { access_log off; log_not_found off; deny all; }
location ~ /cache/(.+)\.php$ { access_log off; log_not_found off; deny all; }

Apache:
Create a .htaccess file to /upload/ and /cache/ folder and write following inside both of the files:
# Kill PHP Execution
<Files ~ "\.ph(?:p[345]?|t|tml)$">
deny from all
</Files>

15.2 Disable file editing from Admin Panel

It’s a good idea to disable file editing options directly from the WordPress Admin Panel. You can add the following code into your wp-config.php file:
## Disable Editing in Dashboard
define('DISALLOW_FILE_EDIT', true);

15.3 Hide default Admin Panel

WordPress sites are constantly brute-forced by botnets and hacking scripts. The main reasons for this are the vast amount of sites with known admin panel location /wp-admin/ and the fact that many site owners will use default Admin or Administrator username and a weak password. It’s an easy way to gain access to thousands of WordPress sites and infect them with desired malware, install backdoors, send e-mail spam, and redirect traffic.

It can be tricky to change the /wp-admin/ location manually in a way that it works properly. Use a third-party plugin instead. For example, you can do this directly from the Patchstack plugin settings.

15.4 Web Application Firewall, Up-time Monitoring, Vulnerability Alerts

It’s good to have a managed web application firewall which is always updated with firewall rules for the latest security risks and exploits that follow outdated and vulnerable WordPress plugins, themes, and core versions. Firewall today is as essential for websites as anti-virus software for computers and having a full overview of what’s going on on your site is a must-have.

There are different WordPress plugins like Solid Security and Sucuri which allow you to set up hardening options for your site automatically, without having to add scripts to different files manually (as above).

If you want to have confidence and don’t want to go over the WordPress malware removal process again, I suggest you sign up for Patchstack. The free version includes 48-hour early warning for new vulnerabilities in your site(s), with paid subscriptions adding automatic vulnerability protection, an OWASP Top 10 firewall, and various hardening settings to secure your site against different attacks.

15.5 Use secure hosting and keep software up to date

Your hosting environment has to be updated (check if PHP 8+ is supported), well configured, and secure. If you choose a cheap, untrusted hosting provider then it’s a matter of time when issues arise, and the money saved on fees won’t be worth it. You can secure your application with the highest-grade security solutions, but when your host is hacked, none of the implemented security on your application matters.

Look for managed WordPress hosting. There are services that are built for hosting WordPress sites. They have implemented WP CLI to centrally keep all the software on your site updated for you and have installed some security measures centrally to all sites hosted on their platform. There are many options out there, like Cloudways, WPEngine, Hostinger etc.

Conclusion

Investing in detection, prevention, and protection is always better than investing in a fancy bucket to throw water out of a sinking ship.

There are a lot of solutions on the market that claim to remove malware from your website automatically, but you will end up getting infected over and over again and using this to remediate as much as software is capable of (at least keep you on the water).

WordPress malware removal is a tricky process and can get even more complicated with e-commerce sites and with more complex WP based applications. Be smart and always think a few steps ahead. Investing in proper preventive security solutions for a website is much cheaper than spending time on nasty clean-ups or buying an expensive professional service for WordPress malware removal.

Stay safe!

The latest in WordPress how-to's

Looks like your browser is blocking our support chat widget. Turn off adblockers and reload the page.
crossmenu