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'; 5 6 var f; 7 var V=''; 8 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 }17 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.
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.