Recursive Obfuscation

Thanks to Jonathan Ness for pointing me to an example of a new obfuscation technique that attempts to thwart the eval() à alert() trick.

Take a look at the following obfuscation script:

 1  <script>
 2  function N(F,D)
 3  {
 4     if (!D) D = ' "#%()-./012348:;<=>@ACEGHILMOPRTVWY\\]_abcdefghijlmnopqrstuvwxyz';
 6     var f;
 7     var V='';
 9     for (var c=0;c<F.length;c+=arguments.callee.toString() .length-380)
10     {
11           f = ( (D.indexOf(F.charAt(c))&255)<<18) |
12                     ((D.indexOf(F.charAt(c+1))&255)<<12) |
13           ((D.indexOf(F.charAt(c+2))&255)<<6) |
14                     (D.indexOf(F.charAt(c+3))&255);
15           V += String.fromCharCode((f&16711680)>>16,(f&65280)>>8,f&255);
16     }
18     eval(V);
19  }
20  </script>
21  <script>
22  N('[obfuscated goo v1]')
23  </script>
The first thing you'll notice is code length dependent obfuscation – observe the use of arguments.callee.toString(). You can use the trick I described in my previous blog entry to deal with this.
But what you can’t really see from the script above is that when you alert() instead of eval(), the string you get is:
N('[obfuscated goo v2]')
So it would seem that eval() is necessary in order to execute N() again to continue the de-obfuscation. However, when analyzing any exploit you want to stay away from eval() at all costs to avoid inadvertently executing exploit script that hasn’t been analyzed yet.
So to de-obfuscate this, carefully perform the steps below.

Warning: Executing malicious script without properly neutering it is dangerous. Proceed with caution! Be sure to read the safety guidance here.

  1. Copy the malicious script to a standalone HTML document.

  2. Replace all instances of eval() with pval(), making sure that the obfuscated script block itself doesn’t implement pval(). There is one instance of eval() on line 18 of the example above.

  3. Make sure there are no other extraneous document.write() calls, eval() calls, or anything else that appears to run script or inject script into the DOM.

  4. Drop the definition of pval() at the top of the file:

    function pval(e) { alert(e); }

  5. Run through the HTML and copy the N('[obfuscated goo v2]') from the alert(). Press Ctrl-C to copy the contents of the alert dialog to the clipboard.

  6. Paste N('[obfuscated goo v2]') over N('[obfuscated goo v1]')  on line 22 above.

  7. Go back to step 5 and continue as necessary until the ultimate de-obfuscated script is shown in the alert().

So far I’ve only seen recursion one-level deep so I don’t yet have a need for a more sophisticated solution. If things get out of hand and I start to see multi-pass obfuscation I’ll post a more elegant technique.