Detailed Post-Mortem of a Website Hack Through WordPress & How To Protect Your WordPress Blog From Hacking

By Angsuman Chakraborty, Gaea News Network
Tuesday, December 25, 2007

This WordPress blog was hacked for few hours on 24th December (nice Christmas present!) from Russia. The hacker exploited several WordPress vulnerabilities in administrative scripts to gain full access to the website (as permitted to apache user), including the ability to upload & run scripts, delete any file owned by apache user, view the file and directories etc. This is a full disclosure on the how the site was hacked and how I detected and removed the hack along with few comments on the state of WordPress security. I added a WordPress plugin and made modifications to prevent any such hacking attempts in future using WordPress. This is a must read for WordPress bloggers.

How the site hacking was detected?

The website was normal. However when I tried to publish or even save a post, it simply showed a blank page. The post was never published or even saved. I knew something was wrong.

My first suspect were couple of plugins which contacted external servers after a publish. I disabled them. I also disabled the ping sites as they were sometimes known to cause problems. None of that helped. I progressively disabled all of the plugins. Even with all the plugins disabled, the post wouldn’t publish. I was left with only one option.

I decided to trace WordPress code to find out the cause of error. I started with the file for post submission - post.php. I found something startling with WordPress code which seriously undermines its security, a flawed design choice but more on it later. post.php calls admin.php which calls wp-config.php which in turn calls wp-settings.php. wp-settings is an interesting file. This file isn’t just about settings. It loads tons of files, loads and executes the plugins and more. The problem was, as I originally suspected, in the plugins but which one? The code which loads the plugins in WordPress is:


if ( get_option('active_plugins') ) {
	$current_plugins = get_option('active_plugins');
	if ( is_array($current_plugins) ) {
		foreach ($current_plugins as $plugin) {
			if ('' != $plugin && file_exists(ABSPATH . PLUGINDIR . '/' . $plugin))
				include_once(ABSPATH . PLUGINDIR . '/' . $plugin);
		}
	}
}

The active plugins, as you can see, are loaded directly with include_once. How do I find the plugin which while loading is causing the script to die?
I added simple syslog() statements before and after the plugin. However it generated copious output from all the traffic.
Remember I was debugging on a heavily trafficked live site. So I added a define in post.php which I was checking before doing a syslog. The debugging code was:
if (” != $plugin && file_exists(ABSPATH . ‘wp-content/plugins/’ . $plugin)) {
if(defined(’TG_ADMIN’)) syslog(LOG_ALERT, “Loading $plugin…”);
include_once(ABSPATH . ‘wp-content/plugins/’ . $plugin);
if(defined(’TG_ADMIN’)) syslog(LOG_ALERT, “Loaded $plugin…”);
}

The result was surprising. The first plugin loaded was not even a plugin I knew existed, let alone use it. It was named ro8kbsmawge.txt. The full path to the plugin was /../../../../../../../../../../../../../../../../../../tmp/ro8kbsmawge.txt
Effectively the file path was /tmp/ro8kbsmawge.txt. A telltale sign of this hacker is the presence of the file ro8kbsmawge.txt in your tmp directory.

I renamed the file and the problem was solved for now. I could publish posts finally. However my site was still not secure against future attacks. I will detail next at how I secured my site and provide more information on the perpetrator and how the site was hacked in the first place.

How the site was hacked using WordPress?

The site exploited a vulnerability in /wp-admin/options.php which allowed it to get the authentication cookies it required to upload the file ro8kbsmawge.txt to my /tmp directory using /wp-admin/inline-uploading.php. It then used /wp-admin/plugins.php to activate the ro8kbsmawge.txt as a plugin while using options-misc along the way.

Finally the hacker accessed the site using his magic word piska233 and browsed few directories on my server before retiring for the day. All of these was done within a span of 3 minutes which leads to the conclusion that a script was used to exploit the holes and orchestrate the hacking.

The full log, except the IP address 217.74.245.85 which was removed for redundancy, of hackers action on my site is:

[24/Dec/2007:07:40:22 -0600] “POST /wp-admin/options.php HTTP/1.0″ 200 1713 “https://blog.taragana.com/wp-admin/options.php” “Opera”
[24/Dec/2007:07:40:24 -0600] “POST /wp-admin/options.php HTTP/1.0″ 302 471 “https://blog.taragana.com/wp-admin/options.php” “Opera”
[24/Dec/2007:07:40:26 -0600] “POST /wp-admin/inline-uploading.php?post=-1&action=upload HTTP/1.0″ 200 1645 “https://blog.taragana.com/inline-uploading.php?post=-1&action=upload” “Opera”
[24/Dec/2007:07:40:29 -0600] “POST /wp-admin/inline-uploading.php?post=-1&action=upload HTTP/1.0″ 200 142 “https://blog.taragana.com/inline-uploading.php?post=-1&action=upload” “Opera”
[24/Dec/2007:07:40:52 -0600] “POST /wp-admin/options.php HTTP/1.0″ 200 1713 “https://blog.taragana.com/wp-admin/options.php” “Opera”
[24/Dec/2007:07:40:54 -0600] “POST /wp-admin/options.php HTTP/1.0″ 302 471 “https://blog.taragana.com/wp-admin/options.php” “Opera”
[24/Dec/2007:07:40:57 -0600] “POST /wp-admin/inline-uploading.php?post=-1&action=upload HTTP/1.0″ 200 1645 “https://blog.taragana.com/inline-uploading.php?post=-1&action=upload” “Opera”
[24/Dec/2007:07:41:11 -0600] “GET /wp-admin/options-misc.php HTTP/1.1″ 200 7764 “-” “Opera/9.24 (Windows NT 5.1; U; ru)”
[24/Dec/2007:07:41:15 -0600] “GET /wp-admin/wp-admin.css?version=2.0.7 HTTP/1.1″ 304 - “https://blog.taragana.com/wp-admin/options-misc.php” “Opera/9.24 (Windows NT 5.1; U; ru)”
[24/Dec/2007:07:41:15 -0600] “GET /wp-includes/js/fat.js HTTP/1.1″ 304 - “https://blog.taragana.com/wp-admin/options-misc.php” “Opera/9.24 (Windows NT 5.1; U; ru)”
[24/Dec/2007:07:41:12 -0600] “POST /wp-admin/inline-uploading.php?post=-1&action=upload HTTP/1.0″ 302 - “https://blog.taragana.com/inline-uploading.php?post=-1&action=upload” “Opera”
[24/Dec/2007:07:41:21 -0600] “GET /wp-admin/plugins.php?action=activate&plugin=/../../../../../../../../../../../../../../../../../../tmp/ro8kbsmawge.txt&_wpnonce= HTTP/1.1″ 200 1474 “https://blog.taragana.com/wp-admin/plugins.php?action=activate&plugin=/../../../../../../../../../../../../../../../../../../tmp/ro8kbsmawge.txt” “Opera”
[24/Dec/2007:07:41:23 -0600] “GET /wp-admin/plugins.php?action=activate&plugin=/../../../../../../../../../../../../../../../../../../tmp/ro8kbsmawge.txt&_wpnonce=7b4c8019bd HTTP/1.1″ 302 - “https://blog.taragana.com/wp-admin/plugins.php?action=activate&plugin=/../../../../../../../../../../../../../../../../../../tmp/ro8kbsmawge.txt” “Opera”
[24/Dec/2007:07:41:30 -0600] “GET /?piska23 HTTP/1.1″ 200 95716 “https://lamer/mwpep/?mode=shell&what=20″ “Opera/9.24 (Windows NT 5.1; U; ru)”
[24/Dec/2007:07:41:36 -0600] “GET /?piska233 HTTP/1.1″ 200 15840 “-” “Opera/9.24 (Windows NT 5.1; U; ru)”
[24/Dec/2007:07:41:44 -0600] “POST /wp-admin/options.php HTTP/1.1″ 302 471 “https://blog.taragana.com/wp-admin/options-misc.php” “Opera/9.24 (Windows NT 5.1; U; ru)”
[24/Dec/2007:07:41:47 -0600] “POST /wp-admin/options.php HTTP/1.1″ 302 471 “https://blog.taragana.com/wp-admin/options-misc.php” “Opera/9.24 (Windows NT 5.1; U; ru)”
[24/Dec/2007:07:41:55 -0600] “GET /?piska233&dira=/tmp HTTP/1.1″ 200 9930 “-” “Opera/9.24 (Windows NT 5.1; U; ru)”
[24/Dec/2007:07:41:54 -0600] “GET /wp-admin/options-misc.php?updated=true HTTP/1.1″ 200 7842 “https://blog.taragana.com/wp-admin/options-misc.php” “Opera/9.24 (Windows NT 5.1; U; ru)”
[24/Dec/2007:07:42:36 -0600] “POST /index.php?piska233&dira=./ HTTP/1.1″ 200 36721 “https://blog.taragana.com/?piska233″ “Opera/9.24 (Windows NT 5.1; U; ru)”
[24/Dec/2007:07:43:23 -0600] “GET /index.php?piska233&dira=./wp-content/plugins/Wysi-Wordpress/themes/advanced/docs/es/images HTTP/1.1″ 200 6506 “-” “Opera/9.24 (Windows NT 5.1; U; ru)”
[24/Dec/2007:07:43:38 -0600] “GET /wp-content/plugins/Wysi-Wordpress/themes/advanced/docs/es/images HTTP/1.1″ 301 298 “-” “Opera/9.24 (Windows NT 5.1; U; ru)”
[24/Dec/2007:07:43:40 -0600] “GET /wp-content/plugins/Wysi-Wordpress/themes/advanced/docs/es/images/ HTTP/1.1″ 200 604 “-” “Opera/9.24 (Windows NT 5.1; U; ru)”

The good news is that no harm was done.

WordPress security issues & design flaw

The basic design flaw is that loading WordPress configuration (required for database access for authentication checks) loads wp-settings which loads tons of other PHP files as well as all the WordPress plugins and any hacks. All of these are done even before you have a chance to authenticate the user. This is extremely dangerous for administration scripts as it allows a hacker to pass arguments to and execute gazillions of WordPress files which may or may not have proper security checks in place. User authentication and entitlement should be done at the very beginning to prevent unauthenticated scripts from proceeding any further. To do that wp-config must be modified to not include wp-settings. It should be separately included where required, even at the cost of redundancy. Administration scripts (under wp-admin) requires only wp-config to get the database details to authenticate the user and identify its entitlements. After authentication the rest should be loaded. This flaw was exploited to get the authentication cookie details which was subsequently used. The exact exploit used in this case is hard to find from just server logs. However it was caused by late authentication problem as described above.

There arewere known issues with both options.php & upload script, some of which are detailed here.

One of the challenges with WordPress is that security considerations was mostly an afterthought (feel free to disagree) which were latched on as WordPress became more and more popular. You have to continuously update your WordPress to keep up with the latest patches.

There are 71 reported security advisories in Secunia (22 reported in 2007) and 9 viruses based on WordPress (one from 2007).

Most likely the site was hacked using the cookies authentication vulnerability as detailed here.

Several WordPress plugins and themes also have security advisories:
- AdSense-Deluxe 0.x (plugin for WordPress)
- AndyBlue 1.x (theme for WordPress)
- Blix 0.x (theme for WordPress)
- Blixed 1.x (theme for WordPress)
- BlixKrieg 2.x (theme for WordPress)
- Blue Memories 1.x (theme for WordPress)
- myGallery 1.x (plugin for WordPress)
- PictPress 1.x (plugin for WordPress)
- Pool 1.x (theme for Wordpress)
- Redoable 1.x (theme for WordPress)

You can read all the Secunia advisories on WordPress here.

Who was the hacker?

The IP address of the user responsible for hacking my site is 217.74.245.85. The IP address belongs to KUBANGSM-NET:

% Information related to '217.74.245.0/24AS29497'

route:          217.74.245.0/24
descr:          KUBANGSM-NET
origin:         AS29497
mnt-by:         KUBANGSM-MNT
source:         RIPE # Filtered

It is owned by:

person:       Volkov Denis
address:      61, Gimnazicheskaya str. 350000, Krasnodar, Russia
phone:        +7 8612 660126
fax-no:       +7 8612 401505
e-mail:       d.volkov@kuban.mts.ru
nic-hdl:      VD370-RIPE
source:       RIPE # Filtered

Their website is https://www.kuban.mts.ru/. They appear to be legitimate mobile & internet service provider. Most likely their internet service is being abused by the spammer. Nevertheless I decided to ban this IP address from accessing my server.

The hacker was most likely using Opera 9.24 browser (almost latest version) on Windows XP (NT 5.1). While user agent can be faked, there is no reason to suspect so in this case. The user agent string also shows that he was using the russian language file of Opera.

Magic Shell by Mag Screenshot

Anatomy of the hacking script

The hacking script is a php script with a a nice comment and TODO line:
/*Magic Include Shell by Mag icq 884888*/
//TODO: ñëèòü ôàéëî íà ñâîé ôòï (!)

Effectively it is a file manager, probably adapted from any of the free php file manager’s on the net. It allows you to:

  • Browse directories and files
  • Edit files
  • Rename files
  • Delete files
  • zip & unzip files
  • Upload & download files & directories
  • Execute arbitrary PHP scripts
  • Execute arbitrary shell commands
  • Provides basic server, system & PHP information

The bulk of the code is executed when it receives a particular query string to a normal HTTP GET request. So while https://blog.taragana.com/ will work as usual, https://blog.taragana.com/?piska233 will open with this magical hidden shell which will expose your entire website to an outside hacker.
Note: There is nothing magical about piska233. It is a password which was most likely allowed to be chosen and given as input to the original script which injected this trojan horse on my site.

A trojan which protects itself against worms…

The trojan takes steps to protect itself against worm! The code at the end which is always executed is:

$post_arr=implode('.',$_POST);
$get_arr=implode('.',$_GET);
$cook_arr=implode('.',$_COOKIE);
$post_arr_key=implode('.',@array_flip($_POST));
$get_arr_key=implode('.',@array_flip($_GET));
$cook_arr_key=implode('.',@array_flip($_COOKIE));
$other_shtuki=@file_get_contents('php://input');
$cracktrack = strtolower($post_arr.$get_arr.$cook_arr.$post_arr_key.$get_arr_key.$cook_arr_key.$other_shtuki);
$wormprotector = array('base64','user_pass','union','select','substring','or id=');
$checkworm = str_replace($wormprotector, '*', $cracktrack);
if ($cracktrack != $checkworm) die("");

It checks for certain keywords (in $wormprotector array) within GET, POST & COOKIE data. When they are present it simply aborts. This is one of the means to detect the current version of the script. This is also the reason why it failed to submit a post. An url such as this would also die - https://localhost/wordpress/?select as would https://localhost/wordpress/?base64.
However there is nothing to be happy about. It will take less than a minute to modify the script and make it immune to detection in WordPress.

How to protect your WordPress site from hacking?

You can protect your administration scripts (scripts under wp-admin), where most attacks originate including this one, by restricting access to your admin scripts to specific IP address or IP address range. You can also add a basic authentication on top of WordPress by using your httpd.conf file or .htaccess. This adds a layer of security which any hacker will have to overcome before he gets to your WordPress vulnerabilities.

WordPress should be upgraded as regularly as possible. However that is easier said than done. Normally I prefer to wait a few versions before the new versions stabilize.

WordPress security vulnerabilities affects every one of us, WordPress based bloggers. While I was fully aware of the possibilities and even thought of writing a plugin to address these issues before, I decided to wait (and address more urgent issues or so I thought) relying on the law of probability to protect my sites; after all there are several million WordPress blogs out there, how much chance of being targeted by a hacker? Well, as it turns out, I was wrong.
To simplify securing WordPress, I have created a plugin, to be released soon, which leverages these ideas and more to secure your WordPress installations. It works with your .htaccess but it also works even when .htaccess facility is not available. I have already started using it on my blogs.

Discussion
December 14, 2010: 5:29 pm

Following my exploration, billions of persons all over the world receive the personal loans at different creditors. Thence, there’s great possibilities to receive a credit loan in all countries.

January 21, 2010: 8:51 am

My blog was hacked twice and I can’t find the right to protect it from hackers :(

June 26, 2009: 6:07 pm

Very interesting post. Thank you for sharing with us.


RaiulBaztepo
March 29, 2009: 12:28 am

Hello!
Very Interesting post! Thank you for such interesting resource!
PS: Sorry for my bad english, I’v just started to learn this language ;)
See you!
Your, Raiul Baztepo


rudy
November 6, 2008: 10:14 pm

What ever happen to the plugin you mentioned at the end of the post?

November 4, 2008: 6:22 am

Danke für die guten Informationen.

June 1, 2008: 5:58 am

[...] to some script-kiddie hacking my aging wordpress (2.0.4) installation I decided to upgrade wordpress on zensonic.dk. At the same [...]

May 7, 2008: 11:10 pm

[...] If you’re curious, you can read about the exploit the attackers used here and here. [...]

May 5, 2008: 5:08 pm

[...] Taragana has a little something on the possibilities of a Wordpress hacking. There are many more on the subject as well. [...]


bronzy
April 23, 2008: 7:08 am

hi,

I wish I had found and read your article before I was a victim to this, after reading this I am sure that this is what possibly happened to my sites, they gained access through wordpress and infiltrated my entire public_html folder and erased all my files and folders, I was hosting 7 websites and all are now gone, and I know you should always backup backup backup, but I was thinking hey, I just got a few little sites, who would want to hack me lol, well now I know better, to little to late. But thanks for this.

April 12, 2008: 4:20 am

great article… thanks :)

March 13, 2008: 11:15 am

Thanks for this. One other suggestion for people running an insecure wordpress with other critical applications, is to run them on different unix userid accounts.

March 5, 2008: 11:34 am

I second dalton’s thoughts… if you’ve got that plugin ready, I will be glad to offer hosting space for you if you’re worried about bandwidth. I’d also love to get it running on some client web sites of mine. For now, I have been running into attempts from similar scripts and would love to chat.

Best wishes and thanks for the work you’re doing.

-Dave

February 28, 2008: 9:28 am

Hi there,
I am wondering if the plugin you mentioned to protect your blog from hacking has been released? I am sure every WordPress blogger and admin would be eternally grateful.

YOUR VIEW POINT
NAME : (REQUIRED)
MAIL : (REQUIRED)
will not be displayed
WEBSITE : (OPTIONAL)
YOUR
COMMENT :