Blog

How to recover a hacked WordPress website

Well it happened to you. You receive an email from a customer, or perhaps your web host : Your WordPress site is displaying an “unsafe website” browser error. Or maybe it is now showing some gambling advertisements. It clearly has been compromised. What do you do now?

Based on over 14 years experience hosting, managing and developing WordPress websites, you can imagine that we have seen (and dealt with) this problem a few times. I’ve decided to write this guide to help others out there in the immediate aftermath of a WordPress site being compromised.

Step 1 : Take your site completely offline

If your site is serving malicious code or is defaced, that’s reason enough to take the entire site offline. Second to that is there may be so many things you dont realize is happening. There could be a web shell or external connections being made to a 3rd party happening.

The easiest way to do this is to go to the root folder (the parent directory from where your wp-config.php file is located) and execute the following command (in linux) :

chmod 000 public_html

What does the command above do? Well it just sets the permissions so that nobody (especially the outside world) can view the contents of your website folder. Dont have shell access to your web hosting environment? No problem. You can simply rename your public_html folder to something else like “public_html_disabled” via FTP or file manager.

Will this stop a PHP shell that is already running from web services? Probably not. Once either of the above changes are complete, you are going to want to fully restart all web services. Dont have access? Ask your web host to restart your web services for you. This will close all currently running connections and force a restart.

Step 2 : Find out when the intrusion took place

This is not always possible or reliable. I say not reliable because depending on the level of privilege escalation, file and folder date modification times can be manipulated. That said, it is not a fruitless effort.

The best place to start is to establish when the last time you modified files. Modifying files could come in the form of uploading an image, updating a plugin (or WP Core) or making changes to your theme.

Start within the window of time and gradually adjust or filter out the files that may be irrelevant

find /directory_path -mtime -1 -ls

This may produce way too many results. You could tweak the query above to only filter files with the php extension ,but you could miss things like malicious .htaccess files :

find . -type f -name "*.php" -mtime -1 -ls

The “-1” in the queries above tells the find command to filter files modified within the last 24 hours (or 1 day). Simply change that to go backward in time further until you have a good idea of an established day/time that the site was infected.

Again this is not always reliable but could be informative especially when deciding how far back you should go in restoring a backup. Another method could be scanning your entire site folder for malware and then examining all the modified date/times of each (confirmed) malware file found. One such tool is the PHP malware scanner.

Step 3 : Establish how the intrusion took place

This is even trickier but having an understanding of what vulnerability or insecurity was exploited to gain access and compromise your site will help to avoid the same thing just happening again once your site is back up.

The clues offered in step 2 above may indicate where the intrusion happened simply from where the malicious files were uploaded. You have to use your intuition as well as some cross referencing of the WP Core and plugin versions you were running with known vulnerabilities to get a good idea of what happened.

Step 4: Restore your site files and database

At this point you should be ready to restore the entire site from backups. This would be a full file + database backup. It is prudent to audit the file backup of the date you are choosing to restore to ensure there are no malicious files or WP users you dont recognize. Depending on what you find here will influence the next steps.

Backups not compromised? Restore the full site file and database backups

Completely move all current site files offline in case further analysis is needed. Make sure not to miss any .htaccess or other hidden files or folders in this process. Then do a full restore of the site backup files and database.

Backups compromised? Dont have backups? Restore entire site by hand

This is not as daunting as it sounds and is a good exercise for anyone to go through at least once. The idea of this strategy is to basically rebuild the entire site filesystem from scratch, reinstalling all plugins manually as well as migrating the uploads folder and your site’s theme. Simply follow these steps :

    1. 1. Move all compromised files and hidden folders offline
    2. 2. Download a brand new wordpress install archive and extract it into the site folder
    3. 3. Restore a database backup from before the compromise or use an (audited) copy of the current site database
    4. 4. Configure the wp-config.php file to point to this database
    5. 5. Migrate the wp-content/uploads folder to the new site. Carefully audit this folder using the find command to filter out files that are not media based extensions :
      find . -not -name "*.jpeg" -not -name "*.jpg" -not -name "*.gif" -not -name "*.png" -not -name "*.webp"
    6. 6. Migrate your site’s theme folder. If this is a downloaded theme, just download it from the source. If this is a custom theme then you may want to scan the theme folder for malware using a PHP malware scanner as well as a manual audit of all files/folders.
    7. 7. Update wordpress core and all your plugins. If one of your plugins hasn’t been updated in over 3 months, remove it and find an alternative

How to lower the risk of your WordPress site being hacked again

Post site compromise : What to do next

The first thing you will want to do is reset all user credentials connected to your website. This includes WordPress users, FTP users and control panel users. If you are managing a shared CPanel server, you could use the following shell scripts that may be helpful in mass credential changes with FTP and WordPress users.

How to mass change all FTP passwords in Cpanel

The script below will go through all users on your cpanel system and reset the password to something completely randommized

#!/bin/sh
export ALLOW_PASSWORD_CHANGE=1
ls -1 /var/cpanel/users | while read user; do
pass=`</dev/urandom tr -dc "A-Za-z0-9*-/+.*=_\|\\#" | head -c16` echo "$user $pass" >> passwords.txt
/scripts/realchpass $user $pass
/scripts/ftpupdate
done

How to mass reset all WordPress credentials in Cpanel

If you are managing a shared server with many different accounts and WordPress sites, this script below may be useful to you. In a scenario where multiple WordPress sites have been compromised on a shared server, it may save some time to completely reset all WordPress user credentials :

#!/bin/sh 

for obj0 in $(find /home/*/public_html/wp-config.php)
do
    root_folder=`echo $obj0 | sed 's/\/wp-config.php//g'`
    USER=$(stat -c '%U' ${root_folder})
    GROUP=$(stat -c '%G' ${root_folder})
    cd $root_folder
    echo "Doing ${root_folder} .. "
    echo $USER
    echo $GROUP
    wp core update --allow-root
    wp plugin update --all --allow-root
    wp core update-db --allow-root 
    chown -R ${USER}:${GROUP} $root_folder
    wp user list --field=user_login --allow-root --debug=false | xargs -n1 -I % wp user update --user_pass="`pwgen -cny1 25 1`" --allow-root --debug=false %
    for obj1 in $(wp user list --field=user_login --allow-root --debug=false)
    do
        pass_gen=`pwgen -cny1 25 1`
        echo "Resetting user ${obj1} with pass ${pass_gen} .."
        wp user update $obj1 --user_pass="${pass_gen}" --allow-root --debug=false
    done
done

The script above will complete the following on a mass scale in a shared environment :

1. WP Core update
2. WP Plugin update
3. File permission sanitization
4. Cycle through every wordpress user for every wordpress site (using WP-Cli) and reset the password

How to prevent this from happening in the future

I will provide some suggestions that would significantly mitigate the risks of a compromised WordPress site happening again.

Regular core and plugin updates

This goes without saying, but it needs to be mentioned. One of the top reasons for a site being compromised is an insecure or outdated plugin, theme or WordPress core installation.

Lock down your WordPress installation

You can modify your site’s .htaccess file to implement IP address restrictions from accessing the administration area. You can also implement things in your wp-config.php file to disallow any file editing or modifications via the WordPress interface :

define('DISALLOW_FILE_EDIT', true );
define('DISALLOW_FILE_MODS',true);

There are many more suggestions that could be made for locking down your wordpress installation that others have covered already. If you are willing to go with a premium WordPress enterprise hosting solution like WPEngine, they will check many of the boxes for you to ensure your installation is secure and regular backups happen.

Regular site backups

Dont just rely on your web host’s backup solution. Look into implementing additional measures such as offsite backups.

Hopefully this guide helps those that are in a difficult situation to methodically move through the process of restoring a compromised site as well as lowering risk of future incidents.