A Guest Post by Spam Killer Daddyman
Spam is usually very straightforward to manage in WordPress blogs. Almost all of it arrives in the form of comments and is quickly (we hope!) dispatched by the admin (either manually or by her spam filter). Some spammers are more devious, exploiting security holes in WordPress or mysql to embed their nastiness directly within the blog.
This blog recently began to show spam in its RSS feed. Interestingly enough, the spam didn’t show up when viewing the feed directly–only when viewing it through a reader like My Yahoo! or Google Reader. The blog had clearly been infected by some sort of malware.
RSS feeds typically deliver a short summary of WordPress posts, either inline (as in Google Reader) or when you hover the cursor over a link (as in My Yahoo!). The symptoms of this particular malware infection are that only the most recent post contains any summary text at all, and rather than displaying an article summary, it recommends the reader “Buy Aristocort…” or some other nonsense.
We searched through the WordPress files and mysql database but found no trace of the spam. We updated WordPress to the latest version, but still the spam persisted. (Anwyn aside: Therefore, the problem had to be in files that were not updated–either in the WP theme files or in the mysql database itself. Gulp.)
After quite some time of sifting through a mysqldump, I finally stumbled across something suspicious in the wp_options table: a string, hundreds of characters long, that looked like a data dump of some kind. The epiphany came when I saw the following characters toward the end of the string:
Alter your perception a bit and read that string backwards:
This is php code. Base64 is a conversion format commonly used to send binary attachments via email. It’s also a great way to hide malicious code. I’ve seen trojans use it before, but I’ve never seen it coded backwards.
Decoding this, we discovered the php code that has been affecting the RSS feed. We also found other bits that were monitoring cookies, collecting system information and other dangerous things like login information.
So how does this backwards code get executed? Well, the answer to that lies in the Akismet plugin directory. In a file named .akismet.cache_< date >.php, there was code with a strrev() function. This instructs the code in the database to reverse itself and become readable. It appears, although I can’t tell definitively without fully deconstructing the code, that this malware leverages the Akismet spam-filter plugin to do its dirty work. I’m sure the author took great pleasure in that.
Why is this only seen in things like Google Reader and My Yahoo? It turns out there’s a regular expression match done for key sites:
(Anwyn aside: In addition to the RSS readers, the Googlebot also sees the damaged code. Thus when I advance-googled for “aristocort” only on my site, Google turned up page after page that it said had that word–but if you clicked on a link, you got an entire category page where, of course, there was no mention of the spam words. Clever.)
If you’re seeing spam in your WordPress blog’s RSS feed and you have any dotfiles in your Akismet plugins directory (.akismet.cache.php, .akismet.cache_< date >.php, etc), then you’re probably suffering the same affliction as this blog. The fix:
1. Back up your WordPress files. This is just to be on the safe side.
2. Back up your WordPress Database. Again, just to be on the safe side.
3. [Disclaimer by Anwyn: We didn't actually install Akismet on any other blog to double-check that the following affected files were not normally present in a healthy Akismet install. We just nuked them from orbit, and my blog and spam filter do not seem to be suffering. However, if you have more than one of these suspect files, you might want to search each of them for this string: strrev. This is the dangerous command and thus the dirty file. Daddyman googled for these kinds of files related to Akismet and found nothing normal--only warnings of spam exploitation--so you're probably safe to delete them all, but we don't know with 100 percent certainty.) Clean up WordPress by deleting any akismet dotfiles (.akismet.*) in the wp-content/plugins/akismet folder.
4. Clean up the database by removing all affected wp_options lines. The easiest way to do this is with phpmyadmin. Select your database, browse the wp_options table, and look for rows that have an option name of rss_< something >. Do not delete any of the following rows: rss_excerpt_length, rss_use_excerpt, rss_language. Delete any rows which have an option_name of rss_< long string of numbers and letters (hexadecimal) >. For example, in our installation an entry beginning with “rss_f541…” contained the base64_decode() string. You can delete any rows that have a name with a similar format, whether or not you see obviously malicious code; it will not harm your RSS.
5. Create a new post and verify that your RSS feed is now displaying correctly. Google Reader does a good job of refreshing on demand. The My Yahoo homepage doesn’t refresh promptly even if you select the refresh option, so you may need to wait a while before getting confirmation there.
6. Delete cookies in the browser(s) which you use to administer your site. This is just to be safe.
7. Change your WordPress password! You may also want to change your main database password. If you do that, be sure to also update it in wp-config.php.
8. Enjoy a spam-free, fully functional RSS service!
Here are two maintenance steps to help keep your blog clean and performing well:
1. Harden your WordPress installation! An ounce of prevention is worth a pound of cure.
2. Periodically purge spam comments from your database. When you classify a comment as spam, it disappears from sight, but the actual data remains in your database. In the case of this blog, which has been up for several years, 75 percent of the database (or about 9MB) was old spam comments. (Anwyn aside: Good Lord.) A simple sql command, run periodically, will remove all of that cruft and help optimize your database. The sql magic can be run either from phpMyAdmin or directly via the mysql CLI. Just connect to your database and execute the following statement:
delete from wp_comments where comment_approved=”spam”;
There are also some WordPress plugins, such as “Delete Spam Daily,” which profess to make this an even simpler process. I haven’t tried them, so be careful.
(Anwyn aside: I hadn’t updated WordPress since it was first installed on this host, years ago. If I had, no doubt whatever back door the spammers came in through would have been eliminated.)