How To Survive A WordPress Infection (In Ten Steps)

Hidden malware <IFRAME> or links in your website? Base64 encrypted PHP code, or JavaScript appended to every PHP or JS file on your server? PHP run-time errors, malware warnings, broken display or back-end? You might be the victim of a WordPress infection. But don’t worry. You can survive! Take a deep breath, and read on.

Here are my ten steps for surviving an infection of your WordPress-powered website, recovering from the attack, and moving forward.

Step 1: Disable the website as quickly as possible.

It is imperative that you disable the website as soon as you notice the infection, for two very good reasons. First, and most importantly, you don’t want to spread malware or any type of un-intended or unknown content to your visitors.

Second, Google and anti-malware scanning services can identify and blacklist your website when they identify it as infected. This shows a warning notice in search results:

And can even include an interrupt page when any infected results links are clicked:

Step 2: Change all credentials

From your MySQL database username and password and FTP accounts to WordPress users, and even your hosting control panel account, consider changing and strengthening all passwords. Don’t forget to update your wp-config.php file with the new database settings.

Step 3: Locate (and classify) the infection.

If you’ve lived in the WordPress environment (or Drupal, or any popular CMS) for long enough, you’ve seen or heard about at least one security hole. Most of these systems and pretty buttoned up these days, but there are very experienced hackers just waiting to prey on a single misgiving. Even without any security holes, there are determined hackers who will brute-force their way through the login system or employ more nefarious tactics to gain entry to your server.

Once they’ve found a module to exploit, they can upload or inject data to traverse your entire file system, automatically infecting target files. Depending on the problems you’re experiencing with your website, you should have some indication of where to look. If your visual interface or other JavaScript-powered elements are broken, look at your .JS script files for unfamiliar code. If you’re experiencing fatal errors in PHP, embedded links or embedded <IFRAME> elements, look at your .PHP or .HTML files.

Here are some examples of infected code:

1
<?php eval(base64_decode('ZXJyb3JfcmVwb3J0aW5nKDApOw0KJGJvdCA[...]
1
2
3
<script>
var ar="v)y{ifu=lg[rETCB}me h>;
s\"/ 0.,tN1:('
1
<iframe src="http://www.randpaul2016.com/facebook/update.php" width="2" height="2" frameborder="0"></iframe>

Step 4: Remove infected code automatically

If you identified the malicious code on your server, and you host in a Linux hosting environment, and you have shell (SSH) access to your server, and you know a little bit about regular expressions, you may be able to disinfect a lot of your files automatically using Linux commands.

This is not for the faint at heart, so please use EXTREME CARE when building these commands. You are instructing the server to traverse every matching file in your directory structure, and edit its contents.

The regular expression looks like this:

1
find folder/ -type f -name "*.ext" -exec sed -i 's/infection code here//g' {} \;
  • folder/ is the path (relative to your current location in shell) that you want to disinfect. If you are in your root folder, you might use “public_html” if you are in a single-domain environment.
  • -name “*.ext” is the file type pattern to match. If you aren’t sure, or if the same code appears in multiple file types, you can omit theĀ entire -name “*.ext” portion altogether.
  • ‘s/infection code here//g’ is the regular expression which replaces infection code here with nothing (there is nothing between the // slashes following the match regex). Don’t forget to escape your special characters (like the forward-slash in the following example).

Here’s one example of a fully built command, which could be executed from a shell terminal. The command below searches every *.php file in the /public_html/ folder (and all subolders), and eliminates the <iframe> string of code from within any of those files.

1
find public_html/ -type f -name "*.php" -exec sed -i 's/<iframe src="http:\/\/www.infection.com\/update.php" frameborder="0" width="2" height="2"></iframe>//g' {} \;

Step 5: Replace theme files

Upload a backup copy of the theme you are using. If you cannot obtain a backup, manually disinfect each .PHP, .HTML, .JS file in the theme folder.

Step 6: Replace WordPress core files

Download the same or the latest version of WordPress from wordpress.org, then upload to your server overwriting any existing files. If you are not using the same replacement version as what’s installed, ensure compatibility with theme and plug-ins. You should consider deleting /wp-includes/ and /wp-admin/ folders from the server first, to ensure no deprecated files from the older version remain on the server (and possibly infected).

Step 7: Replace plug-in core files

Download replacement copies of any third-party plug-ins you might have installed. Download the ZIP archives from wordpress.org, extract, and upload to your server, overwriting any files already in /wp-content/plugins/. De-activate before and re-activate each plug-in after uploading the replacement files.

Step 8: Consider beefing up your theme security

By creating or adding to your theme’s functions.php file, you can add security features to help thwart intruders.

<?php
// Block any attempts to install themes. Why? 
// Hackers can otherwise upload raw .PHP and other files/tools
function __block_caps( $caps, $cap )
{
if ( $cap === 'install_themes' )
$caps[] = 'do_not_allow';
return $caps;
}
add_filter( 'map_meta_cap', '__block_caps', 10, 2 );
 
// Remove the version number of WP from output. Why? 
// Hackers can target known vulnerabilities by version
remove_action('wp_head', 'wp_generator');
 
// Obscure login screen error messages. Why? 
// The default note can be used to identify a valid username
function wpfme_login_obscure(){ return '<strong>Sorry</strong>: That account information was not valid.'; }
add_filter( 'login_errors', 'wpfme_login_obscure' );
 
// Disable the theme and plugin file text editor in the Appearance menu. Why? 
// Intruders can otherwise add PHP or other malicious code easily
define('DISALLOW_FILE_EDIT', true);

Step 9: Consider beefing up your .HTACCESS security

If you are able to locate the IP address of the intruder (look at access_logs and error_logs with a fine-tooth comb, and compare date stamps to the date stamps of infected files) you can prevent their further access to your hosting environment with these additions to .htaccess:

1
2
3
4
5
6
<Limit GET POST PUT>
 order allow,deny
 allow from all
 deny from 1.1.2.2 # this is the offender's IP
 deny from 1.1.2.3 # repeat for additional IPs
</Limit>

Likewise, this will limit access to your /wp-admin/ folder to just the IPs specifically mentioned. It must be placed at /wp-admin/.htaccess (not your root) folder:

1
2
3
4
5
6
<Limit GET POST PUT>
 order deny,allow
 deny from all
 allow from 192.168.1.1 # this is your external IP address
 allow from 192.168.1.2 # repeat for additional IPs (home, office, employee, etc.)
</Limit>

This will not allow .htaccess and wp-config.php to be output or accessed via browser:

1
2
3
4
5
6
7
8
9
<Files wp-config.php>
 order allow,deny
 deny from all
</Files>
 
<Files .htaccess>
 order allow,deny
 deny from all
</Files>

This will disable browsing directory listings in the case that the default index.php or index.html does not exist:

1
Options All -Indexes

This will disable the server from identifying itself:

1
ServerSignature Off

Step 10: Consider installing security plug-ins

Better WP Security is my favorite all-encompassing security plug-ins that can help to secure your WordPress website. It employs some of the same protection techniques used above, and more, not to mention detecting and preventing intrusion attempts.

Limit Login Attempts does exactly what it says: if someone is attempting to crack a password or brute force the login system, this plugin will stop them (and optionally alert you)

Step 11: Notify Google (if they caught it)

If you were unlucky enough to be flagged by Google before you were able to disable the website, you need to instruct Google to review your website and remove the malware flag.

Login to Google Webmaster Tools and locate the profile for your website. If you don’t already use Webmaster Tools, be embarrassed!! Then, add your website profile and wait for its details. If you’ve been flagged by Google, a notice will appear in your account. You’ll see a button to “Request a Review”, and Google estimates it can take up to a day or longer before your site is reviewed and the malicious classification adjusted.

Other suggestions? Let’s hear about it in the comments!