Haxx0red!

O NOES!!!1!
In the interests of full disclosure, I’d like to announce that my blog was cracked into in the wee hours of the morning (PDT) two Saturdays ago (9/21/2007). I have yet to figure out the vector the crackers used to compromise my blog, but I will repost the details that I have found out so that others may be on their guard against such attacks.
Here’s how I figured out I’d been cracked.

As you may already know, the theme that I am currently running is one that I developed myself (and even plan to release some day…) and so I constantly tinker with its innards. I keep all of the code in a Subversion repository and make semi-regular check-ins in the interests of keeping myself honest and making sure that I don’t lose old changes.
Last Saturday (9/28), I noticed that my copyright statement in footer.php was still referring to 2007, so I quickly edited the file and then did an `svn status` prior to committing my revision. I usually execute a status before committing so that I can recall which files I’ve touched and thus present more meaningful commit log messages. When I executed the status, I noticed something a little funny:

$ svn status
?      functions2.php
?      2590.php
?      styles/phskyline
?      styles/move
?      styles/index.html
?      styles/phskyline.css
?      styles/default/phillyskyline.jpg
M      footer.php
?      layouts/index.php
?      images/o.txt
?      images/faq.php

Note the last two lines — I certainly did. (For you svn newbs out there: the “?” means that Subversion doesn’t know about the files, the “M” means that the file is locally modified) The other files I expected to see there, as I have a couple of page-/post-specific files (2590.php, functions2.php) that I use as templates for my blog alone and thus would be of no use to others and the others were either abandoned or nowhere near complete custom templates. I was 100% certain, however, that I had not created anything in the images directory in a long time. My curiosity aroused, I issued an `svn diff` and noted the following output (pay attention to line 10):

$ svn diff
Index: footer.php
============================
--- footer.php (revision 131)
+++ footer.php (working copy)
@@ -63,6 +63,7 @@
	</div>
	<div id="footercredits">
	<p><a href="<?php bloginfo('url'); ?>"><?php bloginfo('name'); ?></a> is powered by <a href="http://wordpress.org">WordPress</a> <?php bloginfo('version'); ?> and <a href="http://literalbarrage.org/blog/code/elbee-elgee">Elbee Elgee</a></p><p>&copy; 2006 Doug Stewart</p>
	+<?php include('images/faq.php'); ?>
	<?php /* Try. to understand */ ?>

Now I knew something fishy was afoot. I generally don’t just include() files willy-nilly and I was certain that I hadn’t thrown a “faq.php” into my images directory.
So I fired up vi and took a look at faq.php:

<?php
$gg_subnets = array ("74.125.0.0" => 16, "166.90.148.64" => 28, "206.186.136.192" => 26, "209.185.108.128" => 25, "209.245.184.136" => 29, "209.247.159.144" => 28, "209.249.73.64" => 29, "209.85.128.0" => 17, "216.239.32.0" => 19, "216.33.229.144" => 29, "63.211.200.72" => 29, "63.84.190.224" => 27, "65.214.112.96" => 27, "64.124.229.168" => 29, "64.124.229.0" => 24, "64.128.207.160" => 28, "62.140.244.0" => 24, "64.154.178.208" => 28, "64.233.160.0" => 19, "64.68.88.0" => 21, "216.109.75.80" => 28, "64.68.64.64" => 26, "64.41.221.192" => 28, "65.221.133.176" => 28, "216.235.136.72" => 29, "69.224.31.0" => 24, "65.196.235.32" => 28, "65.204.68.160" => 28, "65.210.56.208" => 28, "65.211.194.96" => 28, "216.33.229.160" => 29, "65.214.255.96" => 28, "65.214.255.0" => 24, "65.223.8.48" => 28, "65.245.24.8" => 29, "66.102.0.0" => 20, "66.192.134.32" => 28, "66.249.64.0" => 19, "67.126.100.8" => 29, "67.69.26.16" => 29, "69.111.141.0" => 24, "69.224.21.208" => 29, "69.228.70.0" => 24, "76.220.105.184" => 29, "76.246.222.104" => 29, "69.228.76.0" => 24, "69.236.33.64" => 29, "69.237.120.224" => 29, "71.130.103.0" => 24, "71.130.34.0" => 24, "76.200.97.96" => 29, "72.14.192.0" => 18, "75.17.48.200" => 29, "75.23.57.184" => 29, "75.37.253.0" => 24, "75.42.142.144" => 29, "75.52.244.0" => 24, "75.52.246.0" => 24, "75.52.248.0" => 24, "8.6.48.0" => 21, "75.30.189.200" => 29, "75.52.142.128" => 29, "72.14.224.0" => 21, "62.140.233.0" => 24);
$gg_cip = ip2long($_SERVER['REMOTE_ADDR']);
$gg_show = 0;
foreach ($gg_subnets as $gg_cnet => $gg_mask) {
	if ($gg_cip>>(32-$gg_mask) == ip2long($gg_cnet)>>(32-$gg_mask)) {
		$gg_show = 1;
		break;
	}
}
if ($gg_show) {
	include("o.txt");
}
?>

Aha! Now we’re on to something! The above script appears to check incoming requests to the webpage for a known set of IPs/IP ranges and, if a known one is detected, it displays the contents of o.txt in-line. I will not reprint the contents of said file here, as that would give the dirty spammers the Google Juice they so obviously desire. Suffice it to say that there were links to 1) illegal MP3 sites 2) cigarette-vending sites and 3) sites offering cut-rate Viagra. If you’re really all that interested in the contents, drop me a line and I’ll send it along.
So we’ve got the what (two new files and an alteration of one of mine) and the why (propagation of spam links, potential markers for other blog spammers to note that my blog was compromisable) but I’m still without the how. In response to this breach, I’ve recently updated a lot of the security settings in my hosting environment to hopefully minimize any server-side cracks, I’ve upgraded WordPress to 2.3 (there’s a post in the works on that one…) and I’ve begun looking at my theme code in the hopes of removing any potentially dangerous bits from my own codebase. The offending code never made it into my code (r134 effectively nuked the issue), but I am heartily glad that I was using Subversion to keep track of my files, otherwise it could have been months before I noticed anything out of sorts.
If anyone out there has any further information on this particular scam operation (the Google search results I managed to find were very thin gruel indeed, revealing only two potentially compromised sites, one of which apparently already took care of the issue, as the flaw only shows up in the cached version of the page), please contact me ASAP. I’d like to get to the bottom of what’s going on here. Oh, and if your IP address shows up in that list, you got some ‘splainin’ to do, Lucy.

4 Comments

I’ve just discovered I’ve been hit by the same exact problem on several of my domains. I found one instance of the include-to-.txt and removed it. But spam links are still showing up. I’ve grepp’d my entire account for everything possible, and can’t find the culprit. Really really frustrating. Would love to know if this is a WP issue, or just general hackery (I’ve updated to WP2.5).

Dan:
What version of WP were you running previously?
I’ve been operating under the assumption that the vector was WP’s XML-RPC interface, as that’s the only real point of entry for my setup (I also altered my admin password in an attempt to narrow down exploit holes).
It almost seemed as if the spammers in question got ahold of my login credentials somehow and used the Theme Editor to add the offending code.

Do either of you happen to use a plugin to stop multiple login attempts?
If not, it could be that they brute forced your password.

Feesh:
Blocking multiple logins is a bad idea, as some malefactor could well block you from your own blog simply by repeatedly attempting to log in as “admin”, thus denying you access.
coffee2code’s Last Logins plugin may be something to look into, as it at least logs all attempted logins, thus giving you an idea as to whether someone is attempting to get in and react in time (by changing passwords, etc.).