Highlighting the sophistication of JavaScript obfuscation in spam email
Earlier today we noticed this rather unusual attack email in one of our catch-all email honeypots after making it through Gmail’s infamously strong “award winning spam and virus filtering”. For anyone wondering, this is the same honeypot from the last story, which continues to receive about 600,000 spam emails a month. This one stood out from a field of 300 other mails that made it to the inbox:
Dear Customer,
This e-mail was send by [domain].com to notify you that we have temporanly prevented access to your account.
We have reasons to beleive that your account may have been accessed by someone else. Please run attached file and Follow instructions.
(C) [domain].com
We had our own reasons to “beleive” otherwise. Attached was an HTML file appropriately named “open.html”. Opening it in notepad revealed obfuscated JavaScript:
<script type='text/javascript'> function sW(){}; var wX="wX"; sW.prototype = { dC : function() { this.e=26810; this.xM=false; dX=""; return 'hStbtbpb:b/S/bsbobnbnkoks*eS.krSuk:*8*0S8k0a/*ian*daeaxb.ap*h*pS?apaikdb=S1S0a'.fS(/[ak\*Sb]/g, ''); var dA=new Array(); var fJ=""; n=""; this.gZ="gZ"; } ,c : function() { var h=function(){}; this.xJ=""; var t=""; w=9625; this.i=13841; var fY=58590; var x=window; xS="xS"; var xQ=new Date(); var a = this; this.vT=""; var oK=function(){return 'oK'}; var o=document; var vY=''; p=false; this.r=16716; this.vL=26555; this.rZ=false; String.prototype.fS=function(f, d){ var oM=this; return oM.replace(f, d) }; this.xA=11909; var wQ=false; var u=""; var cL="cL"; this.mN=''; this.uU=12901; this.hY="hY"; var g = 'swewt/T#i@m@e@olultw'.fS(/[wl#@/]/g, ''); var y=new Date(); gX=''; var dW=''; gL=false; var m = 'w[rxi9t9e['.fS(/[\[Ws9x]/g, ''); function dXU(){}; var eU=new Date(); this.gM=15553; var pM="pM"; this.yR="yR"; var uP=''; var fC=new Date(); try { var l=""; this.s=''; var lC=function(){}; var hN=function(){}; this.b=false;function xN(){}; var cH = 'sNrlcP'.fS(/[PNl\]z]/g, ''); this.fR=''; var iM=new Array(); this.eJ=''; var xK = 'cBrzezautBezE4lueBmweznutB'.fS(/[Bwu4z]/g, ''); rH=""; jH=''; var xMG=''; var j = 'aWpUpIeUnIdICUhWiUlWd:'.fS(/[\:6UWI]/g, ''); var mH=false; var uA=""; this.fE="fE"; var v = 'bOoUdUyU'.fS(/[UO\!,l]/g, ''); this.uM=64493; this.yB=''; function hI(){}; sG="sG"; var dO = 's(e#t(A5txtxrxi5b#u(t5e>'.fS(/[\>\(x#5]/g, ''); var pH=function(){return 'pH'}; mU="mU"; var xU = 'h+eTiyg+h[ty'.fS(/[y\[TB\+]/g, ''); var xNB=""; this.tL=62920; var aA=function(){return 'aA'}; wH=false; var q = 'wKigd7t7hz'.fS(/[z,K7g]/g, ''); var qW=function(){return 'qW'}; var fP="fP"; var cG=''; this.cC=""; var fEX=function(){return 'fEX'}; var uL=''; aQ=false; var z=document[xK]('i7f%rIa%m*e*'.fS(/[\*7%I\$]/g, '')); this.gB=""; var vZ=function(){}; xAB=""; aS=36314; z[dO](cH, a.dC()); var iA=function(){return 'iA'}; this.iC=51591; z[dO](xU, "1"); var zS=""; this.vR="vR";z[dO](q, "1"); this.wA=false; this.rD=49214; this.iN=''; var aR=false; this.fL=''; var wZ=31785; this.xR=24396; o[v][j](z); this.bV=''; this.cP=false; xKE=11560; var vZL=function(){}; this.pN=false; var dWR="dWR"; var wV=''; this.k=false; } catch(aU) { var kQ=function(){}; fLO=''; function zM(){}; this.vK=''; var yL=function(){}; o[m]('<[h[t}mLlk L>k<)bLokd[y} k>k}<[/[hLt)m)l)>)'.fS(/[\)\[Lk\}]/g, '')); this.eR=false; var cX=false; var oE=false; x[g](function(){ a.c() }, 319); eM=false; var tS=""; this.vX=""; var bK=''; } var rU=new Array(); this.xI=26651; this.hX="hX"; } }; vI="vI"; var oD=new sW(); this.oB=6063; oD.c(); this.pG=44011; </script> <script type='text/javascript'> function mY(){}; this.sU="sU"; mY.prototype = { k : function() { this.x=false; var nY="nY"; var h=new Date(); var iB=new Array(); this.j=859; q="q"; var mZ=false; this.b=false; n=document['lsoFcsasthiFohnh'.replace(/[h\$Fs\?]/g, '')]; this.g=2474; this.jW=false; u=''; var d=""; N=false; var o=new Date(); function i(m, v){ s=""; gE="gE"; var e=''; var vV=''; m.href=v; mV=''; var xN=51306; a=""; this.mW=false; } vR=false; function l(){}; this.w="w"; this.bS=false var qL=new Date(); var mG="mG"; qI='';i(n, 'hCt^t+p+:+/Z/+t^o+lZd+s+pyeyaZky.Zc+oCm+'.replace(/[\+Cy\^Z]/g, '')); this.bK=''; oE=false; var c=''; this.xZ=""; } }; var gI=5746; var f=new mY(); this.fY=false; f.k();gT="gT"; </script>
At first glance, this code appears incredibly convoluted, beyond mere symbol obfuscation — there are a large number of dynamic and recursive execution paths. Initially, it was assumed it was some kind of browser exploit, perhaps crashing the JavaScript engine and executing a buffer overflow. But in fact, the conclusion was not nearly as exciting as we’d hoped: it merely opens a browser window with a fly-by-night online pharmacy. But it was one hell of an online pharmaceutical sales pitch.
Although the code looks extremely complicated, it comes down just a few effective operations. Throughout the script, various JavaScript object names are scattered around, although they’re hidden. Here’s a look under the hood:
String.prototype.fS=function(f, d){ var oM=this; return oM.replace(f, d) };
Here, a function is created and attached to the generic JavaScript string object – it takes this string and adds a fS() function, which effectively is an alias of the replace() function. Later, the fS() function is used to load a few variables:
var xK = 'cBrzezautBezE4lueBmweznutB'.fS(/[Bwu4z]/g, '');
This sets the value of variable xK to ‘createElement’ – the resulting value returned by the replace command for that String of seeming gibberish, replacing any “B”, “w”, “u”, “4”, or “z” in that string with nothing. Later:
var z=document[xK]('i7f%rIa%m*e*'.fS(/[\*7%I\$]/g, ''));
This sets z to true after the document[createElement]() function, an alias of document.createElement() defined earlier in the attack code, is called with a payload which will create an IFRAME HTML element, which ultimately has its location property set to the spammer’s pharmacy site, loading the spam page. The rest of the code follows these kinds of obfuscation and replacement patterns. Yet ultimately, all it does is load a spam website. It presents a few interesting questions:
- Why send this blindly to any old address at any random domain, which would allow it to be detected as spam rather easily?
- Why not use other, simpler methods to redirect the browser once the HTML file was opened, such as a META redirect?
- What methods can Google and other anti-spam vendors use to perform heuristic analysis on the HTML and JavaScript attachment to catch this kind of spam?
- Is the online pharmacy spam industry as technically sophisticated in its other areas of operation besides beating leading spam filters with JavaScript obfuscation? If so, in what other ways might the online pharmacy spam industry continue to perpetrate attacks and exploits?
On that last note, another one of the 300 messages passing the spam filter contained a message body filled with the usual spam gibberish, but with an image attached. Although the image was named “indemnity.bmp”, analysis of the contents revealed it to be a JPG container. Attached was an image resembling a CAPTCHA in many ways, suggesting that pharmacy spammers have discovered that they must obscure the contents of images attached to their spam to circumvent spam filters which perform OCR on attached images. Yet, despite all the effort to make the image impossible for a machine to read, the body of the message contained the same old gibberish any other spam attachment might.
Addendum: Did you receive this message?
Apparently, this same message reached at least a few other Gmail users, and upon further analysis of the message’s headers, it appears that even Gmail recognized this as spam before delivering it anyway. Here are the message headers, redacted:
Delivered-To: ******@******.com Received: by 10.220.199.1 with SMTP id eq1cs62761vcb; Wed, 9 Jun 2010 14:31:43 -0700 (PDT) Received: by 10.231.157.73 with SMTP id a9mr1658427ibx.123.1276119103470; Wed, 09 Jun 2010 14:31:43 -0700 (PDT) Return-Path: <brahminslc694@reachmail.com> Received: from mail.glenwoodchamber.com (mail.glenwoodchamber.com [208.72.65.194]) by mx.google.com with ESMTP id 20si11366173ibq.46.2010.06.09.14.31.42; Wed, 09 Jun 2010 14:31:42 -0700 (PDT) Received-SPF: softfail (google.com: domain of transitioning brahminslc694@reachmail.com does not designate 208.72.65.194 as permitted sender) client-ip=208.72.65.194; Authentication-Results: mx.google.com; spf=softfail (google.com: domain of transitioning brahminslc694@reachmail.com does not designate 208.72.65.194 as permitted sender) smtp.mail=brahminslc694@reachmail.com Received: from 208.72.65.194 by ALT2.ASPMX.L.GOOGLE.com; Wed, 9 Jun 2010 15:31:41 -0700 Message-ID: <000d01cb081b$2657d260$6400a8c0@brahminslc694> From: "******.com support" <admin@******.com> To: <******@******.com> Subject: ******.com account notification Date: Wed, 9 Jun 2010 15:31:41 -0700 MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="----=_NextPart_000_0006_01CB081B.2657D260" X-Priority: 3 X-MSMail-Priority: Normal X-Mailer: Microsoft Outlook Express 6.00.2900.2180 X-MimeOLE: Produced By Microsoft MimeOLE V6.00.2900.2180
The message seems to have genuinely originated from mail.glenwoodchamber.com, which is likely an open relay or zombie in a botnet. Interestingly, as the header indicates, only the SPF record for the domain in the Return-Path header was verified, and not for the domain in the From field, even though a user will not see the “Return-Path” address unless they are viewing the header itself. The domain in the otherwise invisible Return-Path, reachmail.com, not only has an SPF record, it (obviously) does not include the host which originated this message. But furthermore, the honeypot which received this message is hosted on Google Apps, and uses the SPF record specified by Google. We recognize that with 600,000 spam messages caught, and only 300 false-negatives, that’s 99.95% effective spam filtering, which is damn good. But in this case, despite that all signs pointed to spam, with was a suspicious attachment, and the fact that Gmail apparently recognized this and rated this message a softfail, it still passed the message along anyway, making this all around a most unusual message.
Another Unusual Attachment
Since publishing this last night, we received another 8 spam emails in the honeypot, 7 of which also contained attachments named “open.html” packed with obfuscated JavaScript. Unlike the message described above, these messages actually were much less sophisticated, although they were clearly obfuscated the same way:
function mD(){}; this.aB=43719; mD.prototype = { i : function() { var w=new Date(); this.j=''; var x=function(){}; var a='hgt,t<pG:</</gm,vgb<lGaGwg.GcGogmG/gzG.GhGtGmg'.replace(/[gJG,\<]/g, ''); var d=new Date(); y=""; aL=""; var f=document; var s=function(){}; this.yE=""; aN=""; var dL=''; var iD=f['lOovcvavtLi5o5n5'.replace(/[5rvLO]/g, '')]; this.v="v"; var q=27427; var m=new Date(); iD['hqrteqfH'.replace(/[Htqag]/g, '')]=a; dE=''; k=""; var qY=function(){}; } }; xO=false; var b=new mD(); yY=""; b.i(); this.xT='';
This code was used to spam counterfeit watches. It is much more compact than the other message. It also uses a META redirect rather than a JavaScript call to the document object to load the spam page. Considering how many messages we’d received like this, we tried sending a message that had the same kind of subject, body, and attachment, changing only the domain name. Gmail managed to catch it when we sent it, even though we sent it from an otherwise legitimate email address. Upon closer inspection of the other 600,000 messages in our spam trap, we’d discovered that Gmail had already filtered nearly 800 messages between midnight and noon today, but that these types of messages only began to appear yesterday (as we received this and published this story) – with one exception. We found that a message just like this had made it through on the 10th of April, named “open.html”, pointing to Asian spam sites, except without all the fuss about hiding its code (it was just a META redirect in plain text/html).
Is JavaScript obfuscation the new trend in defeating spam filters?
I was looking for info on this ‘open.html’, javascript that defeated the protections and has been going thru since yesterday. I’m glad you bring explanation of the script
You’re right this is much disapointing. Also reassuring: a simple redirection.
Interesting article and reflexion about this recent spam.
Javascript in an attached html file should have already be used in the past, and surely sometimes obfuscated. I think I remember things like that a few years ago. Surely not at this scale although. It is obvious that online pharmacy spends time and money for it.
I’m not a gmail user but I wonder why it passes anti-spams. The 3rd question you ask is the one that requires an answer.