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.
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
Malspam email —
Bestellung_2026.htmlattachment (German-language lure) ↓0-second meta-refresh →
ad.doubleclick[.]net(click-tracking URL, high-reputation Google front) ↓Redirector →
fostercareintheus.optimizationprime[.]com(decodes base64 email) ↓Delivery kit →
bth.startthewave[.]org/a/#<email>(auto-rebrands to victim's company) ↓POST email →
pengajian.muliastudy[.]com/images/edu/u.php→ returns ZIP file ↓JScript loader —
A021185521S210008-11521.js→ self-copy toC:\Users\Public\ktncm.js↓PowerShell dropper —
nlbzl.ps1→ buildsshmvg_01.ps1(reflective .NET load) ↓.NET loader (
03.txt) — anti-analysis + Defender kill + persistence (NVIDIA-themed) ↓RunPE injector (
01.txt) — process hollowing →InstallUtil.exe/MSBuild.exe↓Core loader (
bl.txt) — patches AMSI/ETW (native), multi-layer decryption ↓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.
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).
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.
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.
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\namedUpdate Drivers NVIDEO_<random>, launched hidden viapowershell.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) → ZwUnmapViewOfSection → VirtualAllocEx → WriteProcessMemory → GetThreadContext/SetThreadContext → ResumeThread (with Wow64 variants for cross-architecture). All API names are mangled; notably the CreateProcessA wrapper retains the Portuguese word "criando" ("creating").
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.
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
NtManageHotPatchfromntdll.dlland overwrites its prologue with a stub returningSTATUS_NOT_SUPPORTED(0xC00000BB) — targeting the hot-patch-based AMSI loading mechanism introduced in 24H2.On 64-bit systems, it pattern-scans
ntdll.dllforEtwEventWriteand overwrites the entry point withxor rax, rax; ret, turning all ETW event logging into a no-op.Both use
VirtualProtectEx→WriteProcessMemory→FlushInstructionCache. The loader can receive additional payloads from C2 in three modes: in-memory injection (RunPE intoMSBuild.exe/InstallUtil.exe, no disk write), PowerShell (.ps1via 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.htaopen 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/MSBuildas injection host) and run regular phishing awareness training — the DDNS C2 rotates too fast for IP blocking to matter.
References
Huntress — From Malspam to Fileless .NET Loader
Huntress — IOCs on GitHub (threat-intel)
The Hacker News — Google DoubleClick Abused in New Malspam Campaign to Deliver .NET Loader
MITRE ATT&CK — Process Hollowing (T1055.012) · Reflective Code Loading (T1620) · Impair Defenses (T1562.001)





