Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -654,6 +654,80 @@ Clipboard hijackers are easy to miss if the operator never steals wallet files.
- Validate copied text against **wallet regexes**.
- Replace the victim's address with an attacker-controlled value immediately before paste/exfil.


## NativeAOT + Mixed-Mode .NET malware triage

Some modern .NET implants avoid obvious packers and instead split the framework across **three compilation targets** so analysts must switch toolchains:

- **IL-only .NET Framework** modules: easiest stage. Decompile with **ILSpy** / **dnSpyEx** and recover shared enums, module names, operator verbs, and argument grammar.
- **Mixed-Mode C++/CLI** DLLs: the same PE contains both native stubs and managed logic. Review it twice: native disassembler for exports / trampolines, .NET decompiler for the real code behind them.
- **.NET NativeAOT** modules: expect a large native PE with thousands of stripped runtime functions, a `.managed` executable section, and a `hydrated` section where many strings only become readable after runtime materialization.

### Quick fingerprinting

- **IL-only**: CLR metadata is obvious and types/methods keep meaningful names.
- **Mixed-Mode**: `DiE`, `dumpbin`, or PE headers show both native code and CLR metadata; exported functions may just be tiny `jmp` stubs into managed code.
- **NativeAOT**: imports may look sparse compared to the capability set, while section names such as `.managed` / `hydrated` strongly suggest NativeAOT-style output.

### Mixed-Mode export traps

Do not assume `DllMain`, ordinal `#1`, or the first export is operational. Some sideloaded implants mimic a legitimate DLL export table and leave most exports inert, with the real loader hidden behind a **specific ordinal**. Enumerate all exports, diff them against the legitimate DLL, and single-step the non-trivial stubs.

If the initial access chain is a trusted EXE + local DLL, also review the dedicated [DLL hijacking](../../windows-hardening/windows-local-privilege-escalation/dll-hijacking/README.md) page for search-order and export-proxying details.

### NativeAOT reversing workflow

1. Run normal PE triage (`strings`, `capa`, imports/exports), but expect major blind spots.
2. Look for **packed UTF-16 immediates** in comparisons instead of plaintext verbs. For example, `"get"` may compile into integer compares over constants derived from `0x0067 0x0065 0x0074` instead of a normal `String.Equals` call.
3. Reconstruct **ReadyToRun / EEType / MethodTable** metadata with NativeAOT-aware tooling so virtual methods and type names become navigable again.
4. Recover **rehydrated strings** from the `hydrated` section; on-disk `strings` output may miss URLs, headers, content-types, and operator verbs entirely.
5. Treat missing imports carefully: security-relevant APIs can be hidden in runtime **P/Invoke descriptor tables** instead of the PE import table.

Useful tools:

- [ida-nativeaot](https://github.com/Dump-GUY/ida-nativeaot)
- [ghidra-nativeaot](https://github.com/washi1337/ghidra-nativeaot)

### Per-module AppDomain isolation

Some managed loaders avoid `Assembly.Load` in the **default AppDomain** and instead:

1. Create a fresh AppDomain
2. Marshal a proxy object across the boundary with `MarshalByRefObject`
3. Reflectively load and invoke the module
4. Unload the whole AppDomain after execution

This matters because assemblies loaded into the default AppDomain cannot be unloaded without killing the process. During live response, dump memory **while the module is running** or break on `AppDomain.CreateDomain`, reflection loads, and `AppDomain.Unload` to capture transient assemblies before they disappear.

### Universal module entry points

Modular frameworks often normalize execution by forcing **every** plugin to expose the same export/method name (for example `get_version`) while the loader chooses the path based on the file format or a filename convention (`n-*.dll` as native, everything else managed). When you recover one module, search the working directory for siblings that share the same entry point and delimiter grammar.

### Numbered module updates and startup cleanup

Another useful hunting pattern is **self-update by numbered DLL suffixes** (`module0.dll`, `module1.dll`, ...). Instead of replacing files in place, the agent writes a higher-numbered variant, loads the newest copy, and may hot-swap its own loader DLL after decoding a **Base64 + GZip** payload from the C2. In parallel, some builds wipe the working directory on startup and keep only the transport module, config, and logs.

During triage:

- Sort dropped DLLs by basename + numeric suffix before assuming duplicates are unrelated.
- Carve Base64 blobs and test **GZip** decompression early when tasking/update messages look text-safe but module-like.
- Prioritize first-run filesystem timelines because previously delivered modules may be deleted before the next beacon loop.

### Credential access and lateral-movement clues hidden by NativeAOT

If NativeAOT imports look too clean, search reconstructed metadata and runtime-resolved API tables for high-value pivots such as:

- `ProtectedData.Unprotect` / `CryptUnprotectData` for **DPAPI** abuse under the compromised user context
- `LDAP://RootDSE`, paged LDAP searches, or certificate-validation bypasses for AD reconnaissance / LDAP credential testing
- `WNetAddConnection2`, `WNetCancelConnection2`, `NetShareEnum`, `NetServerEnum`, `NetLocalGroupGetMembers` for SMB spraying, share enumeration, and domain discovery
- Embedded SOCKS5 / WebSocket tunnel parsers that expose a pivot channel after initial access

For DPAPI-specific post-exploitation workflows, see [DPAPI - Extracting Passwords](../../windows-hardening/windows-local-privilege-escalation/dpapi-extracting-passwords.md).

### Traffic decoding shortcuts

When a transport module exposes generic verbs such as `get`, `send`, `upload`, `ws`, or `sendws`, check for a **small fixed XOR key** and text-safe wrappers such as **Base64** before assuming heavy crypto. Reused static headers, fixed URL paths, and custom tokens are often better detection pivots than domains alone.

## References

- [Unit42 – Evolving Tactics of SLOW#TEMPEST: A Deep Dive Into Advanced Malware Techniques](https://unit42.paloaltonetworks.com/slow-tempest-malware-obfuscation/)
Expand All @@ -676,5 +750,9 @@ Clipboard hijackers are easy to miss if the operator never steals wallet files.
- [Check Point Research – VECT: Ransomware by design, Wiper by accident](https://research.checkpoint.com/2026/vect-ransomware-by-design-wiper-by-accident/)
- [Libsodium documentation – ChaCha20 stream cipher APIs](https://doc.libsodium.org/advanced/stream_ciphers/chacha20)
- [RFC 8439 – ChaCha20 and Poly1305 for IETF Protocols](https://www.rfc-editor.org/rfc/rfc8439)
- [Check Point Research – Cavern Manticore: Exposing an Iran-Linked Modular C2 Framework](https://research.checkpoint.com/2026/cavern-manticore-exposing-iran-linked-modular-c2-framework/)
- [ida-nativeaot – IDA Pro plugin for .NET NativeAOT](https://github.com/Dump-GUY/ida-nativeaot)
- [ghidra-nativeaot – Ghidra plugin for .NET NativeAOT](https://github.com/washi1337/ghidra-nativeaot)
- [Recovering Metadata from .NET Native AOT Binaries](https://blog.washi.dev/posts/recovering-nativeaot-metadata/)

{{#include ../../banners/hacktricks-training.md}}