Skip to main content

Command Palette

Search for a command to run...

From Malspam to a Fileless .NET Loader: Abusing Google DoubleClick and a Five-Stage Evasion Chain

Updated
13 min read
From Malspam to a Fileless .NET Loader: Abusing Google DoubleClick and a Five-Stage Evasion Chain

A German-language "purchase order" email with an HTML attachment is the opening move of a five-stage attack chain in which the malicious link routes through ad.doubleclick[.]net — a high-reputation Google-owned domain most email gateways won't blink at. The Huntress SOC responded to this infection in May 2026 and traced it to a .NET loader that barely touches disk, blinding Windows telemetry before persisting on the victim's machine.

Attack path from malspam to loader

Executive Summary

This malspam campaign is not a run-of-the-mill infection. Before the victim ever reaches attacker infrastructure, the lure routes through Google DoubleClick — a legitimate, high-reputation domain that is rarely blocklisted and frequently allowlisted by email gateways and URL-reputation services. From there, the victim is handed to a self-personalizing malspam kit: it reads the email address from the URL, rebuilds the page at runtime, and pulls the company logo live — no per-organization lure required.

Once the victim interacts, the operation shifts to layered malware staging: HTML → JScript → PowerShell → .NET loader → core loader, leaning heavily on in-memory execution and .NET reflection to stay under the radar. The loader patches AMSI and ETW at the native API level to blind Windows telemetry, hides inside legitimate Microsoft-signed processes (InstallUtil.exe, MSBuild.exe), and beacons to DDNS C2 domains on a non-standard port to keep infrastructure cheap and rotatable. The likely end goal is data exfiltration, with crypto-mining as a possible follow-on. For organizations in Vietnam, the lesson is blunt: filtering email on the reputation of the front domain is not enough — the chain only reveals itself in the later stages.

Timeline and attribution correction

Date Event
May 2026 Huntress SOC responded to a .NET loader infection that began with malspam
2026-06-03 Huntress published the delivery-chain analysis
2026-06-05 Huntress issued a correction: removed the "DesckVB RAT" attribution after review, reassessing it as an unidentified .NET loader

Note on naming: some early reporting (including The Hacker News headline) referred to this malware as DesckVB RAT. Huntress retracted that attribution on 2026-06-05. This article uses the neutral term ".NET loader" per the researchers' latest assessment.

Kill chain

Kill Chain

  1. Malspam emailBestellung_2026.html attachment (German-language lure) ↓

  2. 0-second meta-refreshad.doubleclick[.]net (click-tracking URL, high-reputation Google front) ↓

  3. Redirectorfostercareintheus.optimizationprime[.]com (decodes base64 email) ↓

  4. Delivery kitbth.startthewave[.]org/a/#<email> (auto-rebrands to victim's company) ↓

  5. POST emailpengajian.muliastudy[.]com/images/edu/u.php → returns ZIP file ↓

  6. JScript loaderA021185521S210008-11521.js → self-copy to C:\Users\Public\ktncm.js

  7. PowerShell droppernlbzl.ps1 → builds shmvg_01.ps1 (reflective .NET load) ↓

  8. .NET loader (03.txt) — anti-analysis + Defender kill + persistence (NVIDIA-themed) ↓

  9. RunPE injector (01.txt) — process hollowing → InstallUtil.exe / MSBuild.exe

  10. Core loader (bl.txt) — patches AMSI/ETW (native), multi-layer decryption ↓

  11. C2 — raw TCP + AES/RSA → xtadts.ddns[.]net, afxwd.ddns[.]net

Technical analysis

Abusing DoubleClick + a dynamic malspam kit

Initial access arrives via a malspam email with a malicious HTML attachment, Bestellung_2026.html ("Bestellung" = German for "order/purchase order"). The file contains nothing but a 0-second meta-refresh redirect to a Google DoubleClick Campaign Manager click-tracking URL (ad.doubleclick[.]net/ddm/trackclk/...), bearing campaign/creative identifiers. The next destination is appended after the ?, with the recipient's email base64-encoded in the URL fragment.

The rationale for routing through DoubleClick is filter and reputation evasion: ad.doubleclick[.]net is a high-reputation Google domain that is rarely blocklisted — so the only domain visible at the front of the chain is one defenders are least likely to flag.

Malicious HTML attachment

DoubleClick forwards to fostercareintheus.optimizationprime[.]com/b#<base64-email> — a redirector stage (it does not serve the kit) that decodes the email and forwards to the kit host bth.startthewave[.]org/a/#<email>. Clicking "PDF herunterladen" ("Download PDF") triggers downloadFile(), which builds a hidden form and POSTs email=<address> to pengajian.muliastudy[.]com/images/edu/u.php; the server responds with a ZIP (A021185521S210008-11521.zip).

The clever part: the lure page is a single self-contained HTML file. setupEmailAndUI() reads the email from window.location.hash, extracts the domain, then rewrites the page title, header logo, and footer to impersonate the victim's own employer. The logo is fetched live via a fallback chain (Clearbit → logo.dev → Google favicons → favicone → DuckDuckGo → the company's /favicon.ico). The kit hardcodes no organization-specific content — swapping the email rebrands it instantly. fetchLocationAndTime() calls ipapi[.]co/json/ to print the viewer's city/region/country/local time, reinforcing the "personalized and secure" feel.

JScript loader: evading analysis and self-relocating

The ZIP contains A021185521S210008-11521.js — not the final malware but a multi-stage loader, heavily padded with junk (hundreds of repeated dead functions, garbage Unicode strings, ;;;;; filler, and Portuguese-language comments).

JScript loader contents

On execution, the script checks its own path via WScript.ScriptFullName. If it is running from Temp/Downloads (i.e. just extracted), it copies itself to C:\Users\Public\ktncm.js, relaunches the copy with wscript.exe //nologo, and exits. The second run (from the stable, world-writable location) falls through to the payload-staging logic. The function vjwNvhDoHz() repairs a base64 blob mangled with literal A characters and the 9999 token (to defeat static detection), decodes it into PowerShell, writes it to C:\Users\Public\nlbzl.ps1, and runs it hidden with -ExecutionPolicy Bypass.

The PowerShell stages

nlbzl.ps1 performs, in order: a connectivity check (Test-Connection www.google.com) — if offline, it does not simply exit but calls Restart-Computer -Force; an anti-analysis sweep matching running processes against a blocklist (Dbgview, tcpvcon, tcpview, OLLYDBG, ImmunityDebugger, Wireshark, apateDNS, plus the strings any.run, sandbox, analyze) — same reboot response on a hit; and a payload download via WebClient.

Encoded and obfuscated PowerShell

nlbzl.ps1 acts as a builder: it assembles a further PowerShell script as a string, writes it to C:\Users\Public\shmvg_01.ps1, and launches it. That generated script performs the reflective .NET load ([Reflection.Assembly]::Load), resolves the type ClassLibrary3.Class1, and invokes the method prFVI. The signed Microsoft binary InstallUtil.exe is passed as an argument to prFVI for signed-binary proxy execution.

The .NET loader (03.txt)

The 03.txt payload is a stager: verify it is not being analyzed, disable defenses, download and assemble the next stages, establish persistence, and execute the final payload.

Hardcoded configuration in the .NET loader
  • Anti-analysis: detects VMs/sandboxes (VirtualBox, VMware, Hyper-V, Parallels, QEMU, Sandboxie — including SbieDll.dll, vmhgfs.dll…), triage services (mksSandbox, joeboxserver, triage), cloud/RDP indicators, BIOS registry values containing "virtual"/"Hyper-V", debuggers, and tooling by process name/window title. On a hit, it writes a marker file (vm.txt, Debugger.txt, 01_detect_analisse_process.txt) and exits.

  • Staging directory: creates a deeply nested directory under %UserProfile%\AppData\LocalLow\ with **NVIDIA-themed** names (LocalLow Windows, Program Rules, Program Rules NVIDEO) to mimic a legitimate driver install path.

  • Defender tampering: if no Avast/AVG/Malwarebytes is running, it runs PowerShell to disable real-time monitoring, IPS, MAPS, and sample submission, and adds the entire system drive as an exclusion; it also kills QHActiveDefense (Qihoo 360).

  • Payload retrieval: uses a hardcoded IE8 User-Agent to fetch bl.txt, 01.txt, 02.txt.

  • Persistence: Run + RunOnce keys under HKCU\...\CurrentVersion\ named Update Drivers NVIDEO_<random>, launched hidden via powershell.exe -WindowStyle Hidden; plus a copy in the Startup folder.

Injector (01.txt) + template (02.txt)

02.txt is a PowerShell template with placeholder tokens; after the loader fills in values, it reflectively loads 01.txt (ClassLibrary1.dll) and invokes its Run method. 01.txt is a .NET DLL performing process hollowing via the standard RunPE API chain: CreateProcessA (create suspended) → ZwUnmapViewOfSectionVirtualAllocExWriteProcessMemoryGetThreadContext/SetThreadContextResumeThread (with Wow64 variants for cross-architecture). All API names are mangled; notably the CreateProcessA wrapper retains the Portuguese word "criando" ("creating").

DllImport for VirtualAllocEx with obfuscated parameters

The injection target is operator-configurable: in the statically analyzed sample it was InstallUtil.exe, but in live telemetry Huntress observed MSBuild.exe being used instead.

The core loader (bl.txt) and C2

bl.txt (injected into InstallUtil.exe) is not the final loader but another packing layer: a static constructor reads an embedded resource and decrypts it with a custom stream cipher (32-byte key + 16-byte IV, mixed via multiply-accumulate with magic constants 9495/10476/22014 and shift-XOR), optionally LZX-decompresses, then Assembly.Load. A further layer uses TripleDES-CBC (PKCS7) + GZip. The entire configuration is packed into a single protobuf message, base64-encoded, and stored as one string literal.

Loader configuration

The loader communicates with C2 over raw TCP sockets using AES-encrypted protobuf messages; the AES key is derived from the password P@55w0rd! via Rfc2898DeriveBytes (PBKDF2); RSA handles the initial key exchange; and the loader pins the server identity with five embedded SHA-256 certificate hashes. On the first beacon it collects a hardware fingerprint (WMI: Win32_Processor, Win32_DiskDrive, Win32_BaseBoard, Win32_VideoController, Win32_OperatingSystem), enumerates AV via SecurityCenter2, and specifically detects NVIDIA (GTX/RTX) and AMD (Radeon) GPUs through both WMI and registry — a level of GPU interest that could point to crypto-mining as a follow-on, or be used for masquerading.

Patching AMSI and ETW at the native level

The loader patches AMSI and ETW at the native API level to blind telemetry before it even sets up persistence:

  • It reads the build number; if ≥ 26100 (Windows 11 24H2), it resolves NtManageHotPatch from ntdll.dll and overwrites its prologue with a stub returning STATUS_NOT_SUPPORTED (0xC00000BB) — targeting the hot-patch-based AMSI loading mechanism introduced in 24H2.

  • On 64-bit systems, it pattern-scans ntdll.dll for EtwEventWrite and overwrites the entry point with xor rax, rax; ret, turning all ETW event logging into a no-op.

  • Both use VirtualProtectExWriteProcessMemoryFlushInstructionCache. The loader can receive additional payloads from C2 in three modes: in-memory injection (RunPE into MSBuild.exe/InstallUtil.exe, no disk write), PowerShell (.ps1 via a one-shot scheduled task), or file-drop (AES-decrypt, write to disk, scheduled task).

Indicators of Compromise (IOCs)

IOCs are defanged. Huntress published the full set on GitHub threat-intel.

Indicator Description
Bestellung_2026.html Malicious HTML attachment
fostercareintheus.optimizationprime[.]com Redirector stage
bth.startthewave[.]org Delivery kit
pengajian.muliastudy[.]com/images/edu/u.php Serves the ZIP payload
A021185521S210008-11521.zip Delivery ZIP served by kit (u.php)
A021185521S210008-11521.js, ktncm.js JScript loader
nlbzl.ps1 PowerShell dropper
shmvg_01.ps1 PowerShell stager
zkrbx.txt, gglhn.txt Staging files
andrefelipedonascime1778799406970.2241107.meusitehostgator[.]com[.]br Serves 01/02/03.txt
catalogo.castrouria[.]com Serves bl.txt (packed loader)
%USERPROFILE%\AppData\LocalLow\LocalLow Windows\Program Rules\Program Rules NVIDEO\... NVIDIA-themed staging directory
xtadts.ddns[.]net, afxwd.ddns[.]net C2 servers (DDNS)
P@55w0rd! AES password (PBKDF2) for C2
IE8 UA (Mozilla/4.0 ... MSIE 8.0 ... Trident/4.0 ...) Hardcoded User-Agent for payload retrieval
SHA-256 cert pins (×5) D5B7247C...59B5, C61B1941...01A6, C356AFF1...8C18, F1C3EBE7...5348, E91FB249...74DD

MITRE ATT&CK mapping

Tactic Technique ID
Initial Access Phishing: Spearphishing Attachment T1566.001
Execution User Execution: Malicious File/Link T1204.002 / .001
Execution Command & Scripting: PowerShell / JavaScript T1059.001 / .007
Defense Evasion Obfuscated Files or Information T1027
Defense Evasion Deobfuscate/Decode Files T1140
Defense Evasion System Binary Proxy Exec: InstallUtil T1218.004
Defense Evasion Trusted Developer Utilities: MSBuild T1127.001
Defense Evasion Process Injection: Process Hollowing T1055.012
Defense Evasion Reflective Code Loading T1620
Defense Evasion Impair Defenses: Disable/Modify Tools (AMSI/ETW/Defender) T1562.001
Defense Evasion Virtualization/Sandbox Evasion T1497
Persistence Registry Run Keys / Startup Folder T1547.001
Persistence Scheduled Task T1053.005
Discovery System Information Discovery T1082
Discovery Security Software Discovery T1518.001
C2 Non-Application Layer Protocol (raw TCP) T1095
C2 Encrypted Channel (AES/RSA) T1573

Detection

Detection should lean toward behavior/telemetry, not payload signatures (given the fileless chain + signed-binary proxy + AMSI/ETW blinding). Huntress's two core hunting hypotheses: alert when .js/.vbs/.hta is a child process of explorer.exe; and when wscript.exe runs from C:\Users\Public\ and spawns encoded/obfuscated PowerShell.

Suggested KQL for Microsoft Sentinel / Defender XDR (DeviceProcessEvents, DeviceRegistryEvents, DeviceNetworkEvents):

// 1) Script engine running from a world-writable path then spawning hidden PowerShell
DeviceProcessEvents
| where InitiatingProcessFileName in~ ("wscript.exe","cscript.exe")
| where InitiatingProcessCommandLine has @"C:\Users\Public\"
| where FileName =~ "powershell.exe"
| where ProcessCommandLine has_any ("-ExecutionPolicy Bypass","-WindowStyle Hidden","-File","FromBase64String")
| project Timestamp, DeviceName, InitiatingProcessCommandLine, ProcessCommandLine
// 2) Signed-binary proxy: InstallUtil/MSBuild launched by PowerShell (injection host)
DeviceProcessEvents
| where FileName in~ ("installutil.exe","msbuild.exe")
| where InitiatingProcessFileName =~ "powershell.exe"
| project Timestamp, DeviceName, FileName, FolderPath, InitiatingProcessCommandLine
// 3) Persistence: NVIDIA-themed Run/RunOnce keys
DeviceRegistryEvents
| where RegistryKey has_any (@"\CurrentVersion\Run", @"\CurrentVersion\RunOnce")
| where RegistryValueName startswith "Update Drivers NVIDEO"
| project Timestamp, DeviceName, RegistryKey, RegistryValueName, RegistryValueData
// 4) Beacon to known DDNS C2
DeviceNetworkEvents
| where RemoteUrl has_any ("xtadts.ddns.net","afxwd.ddns.net")
   or (RemoteIPType == "Public" and InitiatingProcessFileName in~ ("installutil.exe","msbuild.exe"))
| project Timestamp, DeviceName, RemoteUrl, RemoteIP, RemotePort, InitiatingProcessFileName

Expert opinion

The most notable thing about this campaign is not the loader — which uses known techniques (process hollowing, AMSI/ETW patching, signed-binary proxy) — but the "borrow the front domain's trust" strategy. Using ad.doubleclick[.]net as the first hop targets an operational weak point in most SOCs: URL/email filtering policies built on domain reputation. When the front-of-chain domain is a Google asset, the gateway almost certainly lets it through, and the malicious payload lives in the later stages that delivery-time inspection misses.

For customer environments in Vietnam — where many organizations rely on reputation-based email gateways and proxies as their primary defensive line — this pattern is especially dangerous. Combining fileless execution + reflective loading + AMSI/ETW blinding leaves signature-only EDR effectively blind. In our analysis team's view, the three controls with the most real-world value here are: blocking the script attachment's ability to execute at the very first stage (GPO), inspecting attachments/URLs at time-of-click rather than time-of-delivery, and detection based on process-behavior chains (script engine → encoded PowerShell → InstallUtil/MSBuild as an injection host) rather than static IOCs — because the C2 uses fast-rotating DDNS, where blocking an IP gets you nowhere.

The German lures and Portuguese code comments suggest a commoditized kit reused across regions — consistent with the trend of malspam becoming more scalable while staying convincing through runtime personalization. Huntress having to retract the "DesckVB RAT" attribution is itself a useful reminder: attaching a malware family name too early invites error, and a good TI report should favor verifiable TTPs/IOCs over an eye-catching label.

Recommendations

  • Force scripts to open in Notepad. Configure a GPO so .js, .vbs, and .hta open in Notepad/Notepad++ by default — neutralizing self-execution at the first stage.

  • Inspect at time-of-click. Deploy an email gateway with attachment/URL sandboxing; enable Safe Links/Safe Attachments (or equivalent) so URLs and files are inspected at click time, not just on delivery.

  • Block script attachments at the mail layer. Quarantine or block script files and macro-enabled Office documents at the gateway — a low-effort control that pays dividends.

  • Harden the email perimeter. Deploy SPF, DKIM, and DMARC to reduce spoofing/impersonation.

  • Detect on behavior, not IPs. Alert on behavior chains (script engine → encoded PowerShell from C:\Users\Public\InstallUtil/MSBuild as injection host) and run regular phishing awareness training — the DDNS C2 rotates too fast for IP blocking to matter.

References

More from this blog