12.5 .NET Managed Malware Analysis & the GOOTLOADER Fileless Infection Chain
Key Takeaways
- A .NET assembly is identified by the CLR header in Data Directory entry 14 (COM Descriptor) of the PE optional header plus a single imported function, mscoree.dll!_CorExeMain, so the PE is managed even though native disassembly of it is meaningless.
- .NET binaries decompile to near-original C# with dnSpyEx or ILSpy, and de4dot reverses common obfuscators such as ConfuserEx, making managed malware far cheaper to reverse than native code.
- Reflective loading via Assembly.Load(byte[]) executes a payload entirely from memory with no file written to disk, which is why .NET is the dominant stager format for fileless attacks.
- GOOTLOADER arrives by SEO poisoning of compromised WordPress sites, delivers a ZIP containing one heavily obfuscated JScript file, and is executed by wscript.exe from the user's Downloads or AppData path.
- GOOTLOADER stages its next-stage payload as hex-like encoded values under HKCU\\SOFTWARE\\Microsoft\\Phone keyed on the username, reassembles the chunks with PowerShell, and persists through a scheduled task named after the user that runs at logon.
12.5 .NET Managed Malware Analysis & the GOOTLOADER Fileless Infection Chain
Quick Answer: Blueprint v4 names both the infection chain of .NET malware / analyzing .NET malware (Domain 1) and fileless malware analysis: GOOTLOADER (Domain 5). A .NET assembly is a managed PE: it carries a CLR header at Data Directory index 14 and typically imports exactly one function,
mscoree.dll!_CorExeMain. Because it compiles to MSIL rather than machine code, it decompiles back to near-source C# with dnSpyEx or ILSpy, and de4dot strips common obfuscators. GOOTLOADER is the canonical fileless chain: SEO-poisoned WordPress pages serve a ZIP containing a single obfuscated.jsfile,wscript.exeexecutes it, encoded payload chunks are written underHKCU\SOFTWARE\Microsoft\Phone, PowerShell reassembles and reflectively loads them, and a scheduled task named after the user provides logon persistence.
Recognizing a Managed (.NET) Portable Executable
Native triage techniques mislead on managed binaries. A .NET sample disassembled in a native disassembler shows a tiny, meaningless stub, and its import table looks almost empty — analysts new to managed malware often conclude the file is packed when it is simply compiled to intermediate language.
The Three Reliable Identification Signals
| Signal | Where to look | Value |
|---|---|---|
| CLR / COM Descriptor directory | PE Optional Header, Data Directory entry 14 | Non-zero RVA and size = managed assembly |
| Single import | Import Address Table | mscoree.dll importing _CorExeMain (EXE) or _CorDllMain (DLL) |
| Metadata signature | Metadata root inside the .text section | ASCII BSJB (42 53 4A 42) |
Additional confirmations: the entry-point stub is a 6-byte jmp thunk into mscoree, the section layout is dominated by .text with little .data, and the #Strings and #US metadata heaps contain readable identifier and user-string data that native strings will surface as clean class and method names.
The Managed Analysis Toolchain
| Tool | Function |
|---|---|
| dnSpyEx | Decompile MSIL to C#, edit assemblies, and set breakpoints for managed debugging — the workhorse |
| ILSpy | Read-only decompiler; scriptable and cross-platform via ilspycmd |
| de4dot | Automated deobfuscator that recovers names and removes control-flow flattening from known obfuscators |
| dotPeek | JetBrains decompiler with symbol-server export |
| monodis / ikdasm | Command-line IL disassembly for scripted triage |
Obfuscation you will meet: ConfuserEx (control-flow flattening, constant encryption, anti-tamper, and anti-debug), .NET Reactor, SmartAssembly, and Eazfuscator. de4dot detects the obfuscator from its watermark and reverses the common transforms; what remains after de4dot is usually readable enough to extract configuration, C2 URLs, and mutex names directly.
Why Attackers Choose .NET for Fileless Staging
The decisive capability is reflective assembly loading:
byte[] payload = Convert.FromBase64String(encodedBlob);
Assembly asm = Assembly.Load(payload); // never touches disk
MethodInfo entry = asm.EntryPoint;
entry.Invoke(null, new object[] { new string[] {} });
Assembly.Load(byte[]) maps and executes an entire program from a byte array in memory. No file is written, so file-based antivirus scanning and application allow-listing that gate on disk paths are both bypassed. The same primitive is exposed in PowerShell as [Reflection.Assembly]::Load($bytes), which is why so many PowerShell droppers are really .NET loaders.
Forensic consequence: the payload exists only in the process address space of a legitimate host (powershell.exe, wscript.exe, msbuild.exe, regsvr32.exe). It is recoverable from a memory image — windows.malfind locates the injected private-committed regions, and windows.dumpfiles or a Volatility 3 VAD dump extracts the assembly, whose BSJB signature confirms it is managed.
GOOTLOADER: The Blueprint's Named Fileless Case Study
GOOTLOADER (tracked with the GootKit family and by Mandiant as UNC2565) is the exam's reference fileless chain because every stage exercises a different artifact class.
Stage 1 — SEO Poisoning as Initial Access
The operators compromise legitimate WordPress sites at scale and inject a conditional content system. A visitor arriving from a search engine on a narrow query — characteristically legal and business document phrasing such as "non-disclosure agreement template [jurisdiction]" — is served a fake forum thread in which a helpful "administrator" posts a direct download link. Direct visitors and non-matching referrers receive the site's genuine content, which is why the malicious page is invisible to casual verification and to many crawlers.
Artifacts: browser history and download records showing a search-engine referrer, a forum-styled URL on an unrelated legitimate domain, and a downloaded ZIP whose name matches the searched document topic.
Stage 2 — The ZIP and the Lone JScript File
The archive contains a single .js file, typically named for the document the victim was seeking. Windows executes .js through wscript.exe on double-click. The script is heavily obfuscated with large volumes of junk code and string-splitting, and later variants have concatenated the malicious logic into legitimate open-source JavaScript libraries so that a reviewer skimming the file sees recognizable framework code.
Artifacts: %UserProfile%\Downloads\*.zip, the extracted .js under Downloads or %AppData%\Roaming, WSCRIPT.EXE-*.pf Prefetch entries, and a .js file path recorded in RecentDocs and Jump Lists.
Stage 3 — Registry Payload Staging
Rather than write an executable to disk, the script writes its next stage into the registry as encoded values:
- Payload blobs are created under
HKCU\SOFTWARE\Microsoft\Phone, in keys named after the current username (commonly<username>and<username>0). - The blob is split across multiple values — frequently around seven — that must be concatenated in order.
- A custom substitution encoding maps letters to hexadecimal nibbles, so the values look like innocuous alphabetic data rather than obvious hex or Base64.
- The FONELAUNCH .NET loader stage reads those values, decodes them, and reflectively loads the final payload — historically Cobalt Strike, GootKit, IcedID, or ransomware precursors — into memory.
[!IMPORTANT] This is the whole point of "fileless." The executable payload never exists as a file. It lives in
NTUSER.DATas data, is reassembled by an interpreter that Windows ships and trusts, and executes inside a signed Microsoft process. Disk-only antivirus scanning, file-hash blocklists, and path-based allow-listing all fail against it. The registry hive is therefore primary evidence, not supporting metadata.
Stage 4 — Persistence and Execution
wscript.exe spawns PowerShell, which creates a scheduled task named after the current account configured to run at user logon, re-executing the PowerShell that queries the registry, reassembles the chunks, and reflectively loads the payload. Long sleep intervals and staged beaconing spread the activity over hours to defeat sandbox timeouts and burst-based detection.
The GOOTLOADER Artifact Checklist
| Artifact source | What to look for |
|---|---|
| Browser history / downloads | Search-engine referrer into an unrelated WordPress domain; ZIP download matching a document-template query |
| Filesystem | Single .js in Downloads or %AppData%\Roaming; $MFT and $UsnJrnl entries for creation and later deletion |
| Prefetch | WSCRIPT.EXE-*.pf and POWERSHELL.EXE-*.pf with execution timestamps bracketing the infection |
| Registry | Encoded value blobs under HKCU\SOFTWARE\Microsoft\Phone\<username> and <username>0 in NTUSER.DAT |
| Scheduled tasks | Task named for the user in C:\Windows\System32\Tasks\; Task Scheduler operational log |
| Event logs | 4688 process creation showing wscript.exe → powershell.exe; 4104 PowerShell ScriptBlock logging capturing the decoder; 4103 module logging |
| Memory | Injected private-commit regions in powershell.exe; BSJB-signed managed assemblies recoverable with windows.malfind and VAD dumps |
Reconstructing the Chain
- Acquire memory first — the reflectively loaded assembly exists nowhere else.
- Export
NTUSER.DATand parse theSoftware\Microsoft\Phonesubkeys; carve the value blobs in order. - Reverse the substitution encoding to recover the encoded assembly, then confirm
BSJBand decompile with dnSpyEx to extract C2 configuration. - Correlate 4688 parent-child chains and 4104 script blocks against Prefetch and scheduled-task creation timestamps to fix the infection time.
- Pivot on the recovered C2 indicators through proxy and DNS logs to scope lateral movement and identify other infected hosts.
An examiner loads a suspicious executable into a native disassembler and finds an almost empty import table, a 6-byte jump stub at the entry point, and the ASCII sequence 42 53 4A 42 inside the .text section. What is the correct conclusion and next step?
Responding to a GOOTLOADER infection, an examiner finds the .js file was deleted, no malicious executable exists anywhere on disk, and antivirus logs are clean. Which evidence source most directly recovers the executed payload itself?
While parsing an exported NTUSER.DAT from a suspected GOOTLOADER victim, an examiner finds about seven values under a key path ending in Microsoft\Phone\jsmith0, each containing long strings of letters with no obvious Base64 padding. What are these values and how should they be processed?