I detail my experiences with a WordPress hack across multiple shared hosting sites, the steps taken to recover WordPress and secure against future attacks.
On the December 15th I discovered a number of my WordPress installations had been compromised. Rather than a concerted attack, this was likely the result of widely available scripting tools that allow ‘crackers’ to exploit known vulnerabilities in out of date WordPress installs. Due to the large number of WordPress installs on my server, and a reluctance to run bleeding edge software, I’d been a little remiss in updating WordPress. While none of my installs were more than a couple of months out of date, 5 of my 8 WordPress installs were infected.
Viewing the source code of my sites in Firefox confirmed that advertising links had been inserted directly into the wordpress PHP code – behind a DIV visibility tag – presumably to increase the Google ranking of trojan or spam sites. Additionally, the SQL databases in which WordPress stores it’s data had, in several cases, had additional users added. These fictitious users were most often called simply “WordPress”.
I’m going to outline in this post the steps I took to deal with this infection and to reduce the chance of future attacks. I’m not a security professional, or even a programmer, so my advice is provided ‘as is’. Implementing these measures won’t protect you from a dedicated hacker – if someone wants to crack your website in particular, especially on shared hosting, no amount of effort will stop them. However, these techniques should recover your site, and make WordPress a little less vulnerable to automated scripting attacks.
Option 1 – The Nuclear Option
After you’ve been hacked, the best way to ensure that WordPress is completely free of nefarious links (which can be hidden in obscure lines of code in your theme, WordPress itself, or even plugins), is to use WordPress’s export feature (Tools -> Export) to download an XML file of all of your posts. You can then set up a new directory on your server, install WordPress from scratch – using a new database, new passwords etc, and import all of your previous posts. Finally you can delete your original WordPress install and database.
This method however, has an enormous number of drawbacks. Not least is that you’ll have to manually copy your uploaded media files to your new wordpress folder – either by downloading them and reuploading them via FTP or SFTP, or by copying them using a shell command like scp. [Aside: If you have shell access to your server, you can login using terminal on OSX, or a programme like Putty on Windows]. You’ll also need to confirm that your .htaccess file rules and permalinks structure are replicated in the new install. Likely too, you’ll have to manually update the location of images in each of your old posts, or run an SQL command from phpMyAdmin to do this. Any customisations you’ve made – and failed to back up – to your WordPress theme, will have to be manually redone. Finally, you’ll need to manually rebuild all of your blogroll and other sidebar links, as these are not included in WordPress’s export.
I was forced to do the above for one of my WordPress installs (actually this site) which refused to work after an update to the latest version (at the time of writing 2.7). This method should be reserved for situations when (the user front end of) WordPress has broken down after a hack and attempted repair, or when you have reason to believe that changes have been made to the SQL database underlying WordPress: specifically, changes you may not be able to find or safely remove.
Don’t forget, you’ll still need to check the content of individual posts, as it’s possible – though unlikely – an attack may have pasted spam links into posts themselves – see ‘Checking for Damage’, below.
Option 2 – The Conservative Option
When it came to most of my WordPress installs, cleaning the infection was less arduous. First of all I upgraded (via SFTP, FTP’s encrypted brother) to the latest version of WordPress. SFTP is available in most FTP clients (E.G.: Filezilla, Fetch), although your host may not support it.
Next I deleted any unusual WordPress users, and changed the passwords of my existing users. You can do this pretty safely in phpMyAdmin, which you should be able to access from your hosts admin panel. Instructions on changing the passwords are available here – wordpress allows quite strong passwords, including symbols. You can generate new passwords using a variety of online services, like this one.
It’s also advisable to change the wordpress ‘admin’ user name to something else, you can do this in phpMyAdmin when you are changing the password – just remember to keep a (secure) copy of your new login names and passwords!
To delete dodgy users simply delete their fields in the ‘browse’ view of the wp-users section of your WordPress database (again in phpMyAdmin). Hint: If you can’t remember your database login and password information, you can find it stored (unwisely) in plain text, in your wp-config.php file, which you can get to via SFTP or Shell.
Next I changed the user passwords of my SQL databases to the strongest allowed by my host. In Dreamhost this is done in the admin panel, by going to Goodies -> Manage my SQL -> Users with Access, and clicking on the user name listed in your wp-config.php file.
Just to be clear, these are not the WordPress passwords stored in my databases (the ones I use to login to wordpress), but rather the passwords of the databases themselves (the ones I recovered from the wp-config.php files). After I’d changed my SQL user passwords, I also need to update my wp-config.php files with the new passwords (so my WordPress installs could login to their respective databases).
So to recap. So far – for each of my wordpress installs – I had 1) reinstalled WordPress, 2) deleted strange users, 3) changed my database user login name and password, both in my host’s database management panel and in WordPress’s wp-config.php.
Checking for Damage
Now I needed to check that there were no remaining stolen advertisements or malware scripts hanging around WordPress. I did this in two ways. First I checked my sites themselves, as the world see them, by surfing to each site in turn, and checking the outgoing links on a variety of pages. Firefox used to have a feature which did this automatically, but this has been deprecated in recent releases. Fortunately you can reinstall it via this plugin. After you’ve installed the plugin and restarted Firefox, just go to Tools -> Page Info -> Links, in Firefox, to see the links present on any page. It’s an excellent way to quickly scan for links that shouldn’t be there.
Second, I checked (as best I could) that there weren’t any strange scripts or backdoors left in any of the WordPress themes or files left over from my update. As this can include an enormous number of files when plugins are taken into account, I completely removed all plugins from my WordPresses, and reinstalled each of them.
Getting plugins set up is now much easier – as the latest version of WordPress allows you to install them directly from the dashboard, without having to download, unpack and upload manually.
Once I’d done this, I checked the remaining files (i.e.: all the contents of my wp-content folder, except my new plugins) in a text editor via SFTP. This can take a while, and it can be difficult to know exactly what your looking for, especially if like me your knowledge of PHP comes from playing around with WordPress. As of right now, I’m not aware of any automatic way to confirm the integrity of these files.
Ok – so at this point I’d updated my WordPresses and cleared out any dross left over from the attack. What could I do to protect from this happening again?
Protecting WordPress From Future Attacks
Database backups won’t protect WordPress, but they could make it a great deal easier to restore my databases after any future attacks.
A variety of plugins have been designed to optimise this process. I use WP-DB-Backup, however backups created via WP-DBManager might be easier to ultimately restore (however this plugin has not been updated for the latest version of WordPress, so use at your own discretion).
With DB Backup, I scheduling an automated emailed backup once a week. I set up a new Gmail account created for this purpose, with a strong password – as these backups include all of my WordPress passwords and content (although passwords are MD5 hashed).
I also created a Gmail filter which lets through all emails containing “WordPress Backup” (the subject in emails created by WP-DB-Backup), as some hosts (including my host Dreamhost) have made Google’s greylist, and emails can get automatically discarded to the spam folder.
Next, I manually backed up my customised themes, something I’d neglected to do as often as I should have.
NB: It wasn’t enough to secure just WordPress. I needed to update and secure other CMS installs on my server, as it only takes one compromised service to infect your server with hideous nasties. For me, these included Gallery 2, MediaWiki, and Moveable Type installs. I also used this opportunity to delete older unused CMS’s and databases which were creating a risk on my server.
NB2: I learnt not to rely on Google to tell me if my sites have been compromised. While, Google do automatically scan pages, advising surfers and webmasters if pages have begun to carry a payload, it’s worth noting that despite 5 of my installs becoming infected Google only notified me of 2 of these penetrations.
In other words, if you haven’t updated WordPress for a couple of months, and or if you’ve discovered an infection on one of your sites / installs, it’s highly advisable to manually check all of your sites.
Securing Access To My Host
The ability to recover after a hack is reliant on access to your host’s (in my case Dreamhost) admin panel. I strengthened my password, and confirmed that my account details – phone number, security questions, associated email accounts etc, were up to date.
Your host may also provide specific security functionality to help you lock down access to administrative functions. For the rest of this section I’m going to list Dreamhost specific security options.
Dreamhost Specific Security
I also manually backed up my entire account.
I checked that no one else had access to my account, in account privileges.
Dreamhost allow you to link your login to their admin panel to a specific IP. As I’ve got a static IP address and only need to access my admin functions from a limited set of locations, I did this in Edit Profile section of the Dreamhost admin panel. In Preferences -> tick ‘ Require email confirmation…’.
NB: If you’re planning this, make sure that your correct email address is saved here, and that it is working. Once you enable this option, you’ll be logged out of your web panel till you confirm access to your IP address by email.
WordPress Security Plugins
A variety of plugins are available to improve the (pretty awful) default level of security in WordPress. I’ll list the ones I found useful, and briefly outline some of their functionality.
1 – WP Security Scan – Scans WordPress directories for appropriate permissions, automates production of strong WordPress passwords. The makers promise new features, including WordPress admin protection and one click folder permissions change, in future releases.
2 – WP DB Backup – See backups section above.
3 – Login Lockdown – protects WordPress against brute force / dictionary attacks. It’s something that you might assume WordPress does by default. It doesn’t.
4 – Bluetrait Event Veiwer (BTEV) – This fantastic plugin provides a widget (which I dragged to the top of the dashboard for instant visibility), letting you know who has tried to login to WordPress (and whether or not they’ve been successful), what emails have been sent from your install, what plugins have been activated or deactived, and lots of other useful stuff.
5 – AsakApache Password Protect – I actually haven’t been successful in enabling this plugin in a dreamhost shared hosting environment, but it may be useful for you. The plugin protects a variety of WordPress folders from third party access. If the PHP environment ever crashes or is horribly misconfigured on your server, as happened to Facebook, this could prevent the outputting of raw code, passwords etc, to anyone who loads the right address.
NB: Be careful with this plugin – if it locks you out of wordpress folders incorrectly, you may find your wordpress install inoperative until you manually remove (via SFTP) both the plugin and all of the .htaccess files it has created.
6 – UTF8 Sanitise – After I updated wordpress across my sites I found a huge number of older posts containing odd characters where accents, umlouts and punctuation should be. What happened is that more recent WordPress versions display characters using UTF8 rather than latin characters. So anywhere I’d cut and pasted from word, the web or acrobat into WordPress, I found that extra characters had been added. UTF8 Sanitise lets you manually resave older posts, repairing their encoding in the process.
You can also do this for all your posts at once using UTF8 Database Converter, but again this hasn’t been updated to work with WordPress 2.7, so I’d be cautious using it (considering the database modifications).
With such an enormous number of blogs being attacked, and such a short time between vulnerabilities being discovered and exploited, it seems the perfect time for the equivalent of anti-virus to emerge – software to actively scan for vulnerabilities and nefarious changes to WordPress, and indeed such scripts are becoming available. One such tool is WP Scanner, which uses a combination of wordpress plugin and website to check for vulnerabilities.
NB: Once again, be careful uploading .htaccess files to wordpress subfolders. On my server, using .htaccess to block outside access of wp-content or wp-admin folders severely impairs wordpress’s functioning.
The most important thing I’ve learned is to update WordPress, as soon as possible after a new update – ideally within days. This is fortunately now a lot easier with WordPress’s automatic update functionality.