A story about obfuscated JavaScript

Release date: 03-10-2009


Some time ago, I was reviewing a malicious HTML file to figure out what it does. First, I tried a dynamic method by loading the HTML in the browser in the virtual machine which is not patched, and observe the system. But this did not work, for some reason. So, the only option to go ahead was to trace and decompose the code.

The code looked obfuscated at a first glance (as usual). There were two arrays included in the html file. One, containing the text of the decoding function, and another one containing the "encrypted" text of the final function. The decoding function is being called with the encrypted text of the final function as a parameter. The list of obfuscations found in this file is the following:

1. The variables are obfuscated and the program code is obviously not formatted (it is contained in one long line).

        wcpteyt=Math.PI;
        wgiui=parseInt;
        haqwuu='length';

2. Keyword substitution is taking place, such as variable='length';array[variable]; it would make the same effect as array[length] (similarly, the 'eval' keyword is being replaced, see an example in (1)).

3. A constant is being evaluated, starting from the Math.PI value, including some arithmetic 'and' and 'or' operations.

        zqboezs=wgiui(~((wcpteyt&wcpteyt)|(~wcpteyt&wcpteyt)&(wcpteyt&~wcpteyt)|
        (~wcpteyt&~wcpteyt)));ortetii=wgiui(((zqboezs&zqboezs)|
        (~zqboezs&zqboezs)&(zqboezs&~zqboezs)|(~zqboezs&~zqboezs))&1);

4. A standard eval('S' + 'tri' + 'ng') sort of trick is being used.

        jfbrvt=eval('S'+'tr'+'ing'+'.'+'fro'+'mCh'+'arC'+'ode;')

5. Another constant is being calculated, this time with the use of the text of the decoder function itself. This is very important step, if we modify the text of the decoder function, we will not get the proper final function.

        fpoxeop=tm;for(txakzua=zqboezs;txakzua<phatis[haqwuu];txakzua-=-ortetii)niqsub+=
        phatis.charCodeAt(txakzua);

        where 'phatis' is the decoder function text

6. The new array is being built from the encrypted one, by use of parseInt(), and the String.fromCharCode, where indexes are calculated with the use of the constant from (5). This calculation seems quite time consuming (see the power operation below), on my quite fast system it tooks several seconds to finish. This is, I think, to avoid the automatic JavaScript sandboxers that could be part of antivirus products.

        for(txakzua=zqboezs;txakzua<aekhmf[haqwuu];txakzua+=azihab)wxcnc+=
        jfbrvt(wgiui(zqboezs+unescape('x')+aekhmf.charAt(txakzua)+
        aekhmf.charAt(txakzua+wgiui(ortetii)))^niqsub);

        where 'aekhmf' is the final function text

It's not possible to change anything in the decoding function because of (5). So how to see the code of the final function ? Well, I use the Firebug debugging plugin for Firefox. As I already know which variable will store the text of the final function, it is obvious to make the debugger stop just before calling it. First, we have to put some newlines into the code, to be able to arrange breakpoints. The below is a screenshot from Firebug stopped where I wanted it to. The left pane shows the variable containing the final funcation.

What do we see there ? As usually, some exploits and shellcode. How can we run this code ? Well, I tried many different ways in JavaScript, but I finally failed to do it. The way I solved it is by taking the unicode-encoded shellcode, and throwing it at a little perl script I wrote, shown below:

open FILE, $ARGV[0];
$string = <FILE>; @unescaped = split '%', $string;
foreach $i (@unescaped) { next if not $i; $i =~ s/u//; @chars = split(//, $i); $hexbyte0 = $chars[2] . $chars[3]; $hexbyte1 = $chars[0] . $chars[1]; print chr(hex($hexbyte0)); print chr(hex($hexbyte1)); } print $unescaped;

This script will produce a nice binary shellcode that can be, for example, loaded in IDA. But when you are really lazy (like me), you could do an even different thing; why not just paste the code into some process' memory and run it/debug it ? That said, I tried to paste it in Olly using some built-in functions, but I failed for some reason. Then I decided to copy the original 'host' executable (which is obviously the 'iexplore.exe' program), and insert the shellcode directly into that file, for example, at offset 0x400 (corresponds to 0x00410000 in memory), and then just load such a file in Olly, change the EIP to that address directly after the file has been stopped in the initial breakpoint, and run it. Worked perfectly ! I was able to see, with Wireshark, where the shellcode connects to, and where it is going to store the downloaded executable. The C&C had probably already stopped serving that executable, so I could not verify what kind of trojan is to be downloaded, but having tha latter information on where the trojan is to be stored, my customer could scan his systems for the presence of this file, to see which workstations have been compromised and downloaded the execuatbable.