I have updated this post based on followup investigations.
This morning I found two independent reports of virus warnings being given for two different websites in the Ozblogistan network.
Given that I upgraded to WordPress 3.4.1 only 2 days ago, it would appear that a 0-day exploit is abroad in the wild.
As a tip for fellow WordPress
victims administrators, here is the quickest way to detect and repair attacks based on modifying WordPress PHP files.
1. Login to your server with SSH
You will need a command line for what comes next.
2. Obtain a clean copy of WordPress
Debian-based Linux distros store their website data in
/var/www. For safety we will work on the clean copy from within our home directory — this will prevent the possibility of accidentally overwriting the operation copy of the code.
email@example.com:/home/admin $ sudo wget http://wordpress.org/latest.zip firstname.lastname@example.org:/home/admin $ sudo unzip latest.zip
You will now have a directory
wordpress in your home directory.
diff to look for abnormalities
diff is a mainstay of the Unix/Linux programmer’s toolkit and is commonly used to compare different versions of a single file. But
diff can also be used to compare directories.
The key arguments we will use are:
- Compare two directories recursively
- Ignore changes in whitespace
- “Quiet” output — just show us which files are different.
So now we type:
email@example.com:/home/admin $ diff -rbq wordpress/ /var/www/wordpress/
We get back a great deal of stuff. What we’re looking for is files that differ between the copies.
Suppose for example that we get a report that
/var/www/wordpress/index.php are different. The next thing to do is to turn to
diff in its standard file-vs-file mode:
firstname.lastname@example.org:/home/admin $ diff wordpress/index.php /var/www/wordpress/index.php
And we see an interesting difference pop up:
This is a scrambled version of an actual piece of malware I found.
eval(base64_decode()) pattern is a dead-certain red flag for malware. There is no earthly reason for honest sourcecode to be stored in this fashion, it is always bad news.
Update: That’s not a complete account. In particular, this attack also inserted code into every theme template, which the above approach doesn’t reveal.
You also need to use
grep to dig out instances of
base64_decode. Search for both independently, because some clever malware inserts random comments to obscure the combination.
4. Replace infected files with clean copies.
So you’ve found out that
/var/www/wordpress/index.php is dodgy. Now what?
The simple answer is — replace it:
email@example.com:/home/admin $ sudo cp wordpress/index.php /var/www/wordpress/index.php
Do this for each file you found in step 3.
Update: You may also find that files have been added. It looks now like I had two different attacks, or perhaps one attack with two mechanisms of operation. The
diff -rbq revealed a series of files with random nonsense names, such as
itrqwx.php, scattered throughout the file system. I deleted these as well after looking in them for malware.
5. Final cleanup
Bad code sometimes lingers in caches, so clear those out. I use wp-supercache and some nginx magic to cache .html.gz files on disk; those need to be flushed to stop serving stale, malware-infected pages.
firstname.lastname@example.org:/home/admin $ sudo rm -rf /var/www/wordpress/wp-content/cache/supercache/*
I also use PHP opcode caching, so I will need to restart PHP as well:
email@example.com:/home/admin $ sudo service php5-fpm restart
And that’s it. I have now scrubbed the installation of an annoying virus infestation.
Update: Do it in the opposite order. No point flushing the on-disk cache until you’ve flushed the running software, as the cache will immediately begin to fill up with new potentially-infected pages.
It’s possible to prevent such attacks by locking down file permissions to deny PHP the ability to modify files. However doing so breaks a lot of the update capabilities of WordPress. My own scheme is that I allow PHP the ability to modify its own files, but no others. I have separate users for nginx and PHP and they are unable to modify each other’s files; and of course neither has sudoer or setuid capability.
It’s a calculated tradeoff. I’m relying on the WordPress project team to prevent abuse of the file-modification permissions from within their software. Given that Ozblogistan has twice so far been auto-hacked, their record is not that great. On the other hand I already have to do a lot of dashing hither and yon to keep my bloggers happy; locking down the files would only increase the number of things I have to do by hand. It’s an unhappy compromise.
Update: I changed my mind because the malware would aggressively metastasise whenever I deleted one part in isolation. Delete file X and file Y is quickly created to replace it. So for now I have locked all PHP files down to the lowest level of permissions that work: read-only for the owner, the group and world (444). That’s stopped the malware from reinstalling itself when I remove only part of it.