Anatomy of a Malicious JavaScript File
Recently, I received a strange email in response to an email I sent a very long time ago that was a complete non-sequitur from the first email. (red flag 1) The email said "I made a couple of edits" with a link to download a file from using FireFox send. (red flag 2, sending with a third party file sharing system would allow them to bypass typical malicious file attachment restrictions that email clients have)
I download the file anyway because the url looks like a legitimate firefox.com url.
The file is a zip with a single javascript file in it called presentation_56565.js, (red flag 3, it would be pretty surprising for any presentation to be a raw javascript file)
I open the file and this is what I get:
Immediately this is fishy. This is clearly not your average minification. This smells like someone is trying to do something they're not supposed to and they don't want anyone to know about it.
The first thing I did is to format this file. It looks weird now but it's trivial for a javascript formatter to handle. Things look a lot less intimidating after formatting the document:
There are exactly 256 of these weird assignments. I went out on a limb and guessed that the fact that there were 256 was not a coincidence.
The next thing I do is I copy these lines into google sheets, split on spaces, sort by the number being assigned. There's one assignment for every number between 302 and 558. This is pretty weird. 302? 558? Even though 256 seems meaningful, it's not clear what 302 or 558 might mean. (to me at least at that point).
Beneath these initializations are the declarations of a couple dozen very long arrays that reference the weird variable names. The vast majority of the file is taken up by the declaration of these constants and the arrays but at the bottom there is some more interesting code.
First, there are several very long block comments that just have lorem ipsum text in it which I assume is just another way of making it harder to figure out what the code is doing. I just delete those. After those are gone, my main goal in this phase is to rename obfuscated elements so that it's more clear what is going on. One function I saw that caught my eye as being simple to understand is this one:
function EaAUggXXIcd(iPNtaZ) { OskUHzVG = ""; for (step = 0; step < iPNtaZ.length; step++) { OskUHzVG = OskUHzVG + String.fromCharCode(iPNtaZ[step] - 302); } return OskUHzVG; }
Aside from a couple of poorly named variables and functions, this is actually pretty straight-forward. It is just iterating over an array (and based on the subtraction operation in the fromCharCode call, it's probably safe to assume that it's an array of numbers) and translating that into a string. Interestingly, before it does the translation it subtracts 302! From the point above, the starting number from the declarations is 302. From looking at the other parts of the file, the arrays that are passed into this function are made up of the weird assignments above which indicates that those numbers are really from 0 to 255 or the numbers that could be represented in an unsigned byte.
Back to the function, this is transforming from an Array of numbers to a string so lets rename this function something more meaningful. Let's say decodeStringFromNumArray just to be verbose about it. Immediately, things are starting to become more clear. I see that this function is being called all over the place. The main problem is that it's being called with arrays of those garbage variables above.
Here's an example of a line that makes extensive use of the decodeStringFromNumArray function:
var yzjKyMvtE = new ActiveXObject( decodeStringFromNumArray(Array(ondfghv, pzsda, fzsda, ivbvdfg)) + decodeStringFromNumArray(Array(zndfghv, vgfhjh, egfhjh)) + decodeStringFromNumArray(Array(mgfhjh, pzsda)) + decodeStringFromNumArray(Array(asfd, jsdgdsf)) + decodeStringFromNumArray(Array(kgfhjh, kgfhjh)) ).expandenvironmentstrings( decodeStringFromNumArray(Array(gsfd, azsda, gsdgdsf, svbvdfg, yvbvdfg, gsfd)) ) + decodeStringFromNumArray( Array(wryasc, wryasc) );
Note: We'll get to the ActiveXObject in a soon.
This is getting better but it's still a pain in the neck to try to figure out what this means because of the crazy cipher above. The next thing I do is construct a series of find-and-replaces in Vim to inline all the numbers.
At this point, I feel that I understand the decode function from above well enough that I'm comfortable pasting it into my Chrome console so that I can use it to decode some of the byte arrays that I see in the file.
After inlining the numbers, I can start to piece out what this is doing:
decodeStringFromNumArray(Array(ondfghv, pzsda, fzsda, ivbvdfg)) => "WScr"
decodeStringFromNumArray(Array(zndfghv, vgfhjh, egfhjh)) => "ipt"
decodeStringFromNumArray(Array(mgfhjh, pzsda)) => ".S"
decodeStringFromNumArray(Array(asfd, jsdgdsf)) => "he"
decodeStringFromNumArray(Array(kgfhjh, kgfhjh)) => "ll"
So they're creating an ActiveXObject with the string "WScript.Shell". ActiveXObject is an Internet Explorer only utility that would allow several different automation objects that would give a web page access to various different Windows programs (e.g. Microsoft Word or Excel), According to a quick Google, “WScript.Shell“ allows JavaScript to issue arbitrary shell commands in Internet Explorer.
Then we see they are calling expandEnvironmentStrings() on the resulting object and passing:
decodeStringFromNumArray(Array(gsfd, azsda, gsdgdsf, svbvdfg, yvbvdfg, gsfd) which equates to "%TEMP%"
In sum, this line is getting the temp directory from the shell's environment variables.
After doing a bit more of the same evaluating of these obfuscated strings, I see that the script:
translates those long arrays I referenced earlier into binary data
writes that into a file,
register that file as a DLL with regsvr32.exe
To be completely honest, at that point, I hit my limit of understanding / desire to poke further. Potentially, it would have been possible to reconstruct the DLL it's creating. It's not clear to me what registering this DLL would do to my system, but I decided to stop for fear that I might accidentally run this code and infect my system.
So there you have it, a little bit of arbitrary code execution from a JavaScript file.
Conclusion
One question I had throughout this process is how this actually would have worked. My working theory is that the goal is to get a user with an old version of Windows to double click the file which would run the file without asking for any additional user permissions and it would register this mysterious dll, doing god knows what. But that’s all just conjecture.
So here are a few of my takeaways from this excursion:
I shouldn't have downloaded the initial file. My "Scam" alarms should have gone off before I downloaded the file. In this case, it went okay because the link turned out to be a legitimate Firefox URL but there's nothing to say that someone couldn't try to create a misleading link that I may fall for in the future.
Only Windows users would be susceptible to this
It looks like even if someone had double clicked the file, Windows would still have put some roadblocks up before it would have allowed the script to execute
Automatic formatting and naming your variables and functions well is good.
Internet Explorer is bad.