Friday, July 9, 2010

New Javascript packer: JSidle

Over the last weeks I worked on a new Javascript packer that incorporates some new ideas. The main goal was to make it useful in penetration testing - thus, all used techniques try to circumvent automated analysis (e.g. used by anti virus products). I did not try to make it especially hard for manual analysis.

The Javascript obfuscator I released some months ago (see here) has been used in some metasploit modules and seems to circumvent detection quite well, although it only implements a simple idea. The new packer works standalone (the old used parts of the existing JS obfuscator from the metasploit framework) and uses encryption in a new way for obfuscation purposes.

Most Javascript obfuscators try to reach their goal through complexity, often using some kind of encoding/encryption, mainly because it's an easy way to circumvent known signatures. The JSidle packers does the same except for one big difference: it does take advantage of the time factor. The packer does not give the whole key to the client, so the original payload is really inaccessible to the anti virus engine. As we do want our browser to access and in the end execute it, there is only one solution: let the browser brute force the key. The browser will try to crack the missing part of the key - all this is implemented in a way that ensures the browser will be successful within a few seconds (depending on the configuration of the packer). This delay cannot be circumvented - every AV engine that needs to see the original payload to flag it as malicious will have to brute force it too. This is just not possible within the tenth of a seconds it has to do that, so it will deliver the script to the browser (because the obfuscation itself is no sign for the code being malicious and users are not willing to wait a few seconds before a website loads). Our victim user however, will generously wait for the script to decrypt and execute. If we target modern browsers, the execution speed of their highly optimized JS engines compared to AV emulation puts the attacker even more in advantage.

The whole concept is described in my article in issue 003 of the HITB magazine (pages 42-47). You can get it here: http://magazine.hitb.org



The source code is available on github: http://github.com/svent/jsidle
You can also find patches for metasploit on that repository, I will try to get them integrated into the framework.

An easy example that can be executed with spidermonkey (using pdf mode, as the web mode does rely on the window object in the executing browser):
user@box $ echo "print('hello world')" | ./jsidle -o test.js mode=pdf speed=10000
user@box $ js <test.js
hello world
user@box $

The resulting JS code will take a short time to execute, as the key has to be cracked.
This is the generated Javascript code (not including the used open source MD5 JS library):
var aens = 'xbrweslznfjzgufvllynehboehht';
                var ielye = '484b0a581019145807555e0911445f4b0f07101e39';
                var inre = '';
                for (i = 0;i<ielye.length;i+=2) {
                        inre += String.fromCharCode(parseInt(ielye.substring(i, i+2), 16));
                }
                var nrra = 'ckwrgey';
                var gnnei = 'ioevnpxed';
                var puen = 'tgrzmhmppsdwooswweo';
                var eetah = ['f6855e60cbaf0f53eac03c3084d54e20','f08646544a74dec80d115aa8d481bd6b','374610dd1228da094904b40169528f28','21f4a8ed182327154deb34bc21dfe7d6','4fb97eac4dbff8f150b770dbc21fc848'];
                var rni = nrra + gnnei;
                var flfnn = false;
                var aaenu;
                var eyur = ["", "", "", "", ""];
                var stts = "abcdefghijklmnopqrstuvwxyz";
                for (i = 0; i < 5; i++) {
                var gblt = 1;
                while (true) {
                        eyur[i] = "";
                        var lss = gblt;
                        while (lss > 0) {
                                var nwg = lss % 26;
                                eyur[i] = stts.substring(nwg, nwg + 1) + eyur[i];
                                lss = Math.floor(lss / 26);
                        }
                        if (hex_md5(puen + eyur[i]) == eetah[i]) {
                                break;
                        }
                        gblt++;
                }
                }
                var aaenu = hex_md5(rni + eyur.join(""));
                var eostl = '';
                for (i=0;i<inre.length;i++) {
                                eostl += String.fromCharCode(inre.charCodeAt(i) ^ aaenu.charCodeAt(i%aaenu.length));
                }
                eval(eostl);

8 comments: