Obfuscated JavaScript Redux

Published: 2008-07-14. Last Updated: 2008-07-14 11:48:19 UTC
by Daniel Wesemann (Version: 1)
1 comment(s)

Since our last diaries on the subject of obfuscated Javascript ([1],[2]), the bad guys have again upped the ante a little to make analysis more difficult. The latest few samples that I analyzed all used codes that employed one or several of the "document.referrer", "document.location" and "location.href" properties as part of the decoding process.

document.location and location.href contain the URL of the currently displayed web page, document.referrer contains the URL of the last page visited before reaching the exploit. Using these variables in the obfuscation scheme means that the exploit will not decode correctly, and hence will not run, when copied to a different place or called from a different website.

While this is making analysis more difficult, it is also making life more complicated for the attacker. Imagine Mr Bad Guy using a large set of domains. Formerly, all of these were probably hosting the same bit of malicious JavaScript. This is still the case - the exploit files themselves are the same - but now, he has to encode these exploits specifically for each URL.  Currently, this seems to often require more attention to detail than Mr Bad Guy is able or willing to muster. Two of the obfuscated scripts that I investigated last week did not normalize location.href to lower case before using it in the decoding process - and while Mr Bad Guy had successfully injected his malicious URL as an IFRAME into hundreds of web pages, he had spelled it ending in .HTM there instead of the .htm that he had used in the code. "HTM" and "htm" are perfectly the same to the web server, but case matters quite a bit if location.href (the URL) is used in the obfuscation cipher. I bet he still hasn't figured out why his sploit didn't work :). 

Here's a quick primer on how to tackle the latest obfuscated Javascript in SpiderMonkey. SpiderMonkey, running from command line under Unix, is still my favorite tool for this analysis. But SpiderMonkey is only a JavaScript engine - it doesn't emulate the browser and doesn't even have a "document" or "location" object.

These can be re-created though. There's many ways to do this, I usually just insert the following code block ahead of the malicious JavaScript

function loc() { }
var location=new loc();
location.href="http://the.original.bad.url/where-the-sploit-came-from.html";

And behold, SpiderMonkey sees a location.href variable with the correct content.

If it so happens that your exploit specimen also makes heavy use of "arguments.callee" and "eval()", consider administering some steroids to your monkey .. Spidermonkey that is. After some tinkering, I came up with the following patch. It should apply cleanly on SpiderMonkey 1.7.0, and will make every call to eval() obligingly print its arguments before running them.

$/tmp/js/src$ diff jsobj.orig jsobj.c
1256a1257
> char *c;
1366a1368,1372
>
> c = js_GetStringBytes(cx->runtime, str);
> if (file && c) {
> printf ("File %s Line %i calls eval with the following parameter:\n%s\n\n",file,line,c);
> }

Try it - this small change alone takes the sting out of most of the obfuscation techniques in use.

1 comment(s)

Comments

Daniel,

Fun and informative post!

I use the following trick to achieve the same result without having to patch spidermonkey (of course, this is detectable, so it has some disadvantages, but it works for me...).

I have a file (eval.js) which contains:
old_eval = eval
eval = function(arg0) {
print("eval arg: " + arg0);
old_eval(arg0);
}

Then, I can run spidermonkey as follows:
$ js -f eval.js malicious.js

Also, a more compact (less keystrokes :-)) way to fix the location is:
location={href: "http://evil.com"}

Diary Archives