Skip to main content

Overview

Havoc can execute .NET assemblies directly within the agent process using the Common Language Runtime (CLR) hosting APIs. This technique, known as “execute-assembly” or “inline-execute”, avoids spawning new processes and enables stealth execution of .NET tooling.
Inline assembly execution uses the unmanaged CLR hosting interfaces to bootstrap .NET code within the implant’s process, with optional AMSI and ETW bypasses.

CLR Hosting Architecture

Initialization

Havoc uses mscoree.dll to bootstrap the CLR: Source: payloads/Demon/src/core/Dotnet.c:524

CLR GUIDs

Source: payloads/Demon/src/core/Dotnet.c:10

Assembly Loading & Execution

Source: payloads/Demon/src/core/Dotnet.c:19

AMSI Bypass

Havoc implements hardware breakpoint-based AMSI/ETW bypass: Source: payloads/Demon/src/core/Dotnet.c:86

Hardware Breakpoint Exception Handlers

Source: payloads/Demon/src/core/HwBpExceptions.c:6
Hardware breakpoints (DR0-DR3) trigger exceptions before function execution. Havoc’s exception handlers modify registers to skip AMSI/ETW calls entirely.

Output Redirection

Named Pipe Capture

Source: payloads/Demon/src/core/Dotnet.c:312

Cleanup & Unloading

Source: payloads/Demon/src/core/Dotnet.c:379
CLR hosting can leave .NET assemblies and AppDomains loaded in memory. While Havoc attempts to unload AppDomains, the CLR itself remains loaded in the process.

.NET Version Detection

Source: payloads/Demon/src/core/Dotnet.c:501

OPSEC Considerations

  1. CLR Loaded in Unusual Process: EDRs detect CLR in non-.NET processes
  2. AppDomain Creation: RuntimeBroker or CLR ETW events
  3. Assembly Loading: Image load events for mscoree.dll, clr.dll, mscorlib.dll
  4. Hardware Breakpoints: DR0-DR3 registers set (detectable via GetThreadContext)
  5. Named Pipes: Pipe creation for output redirection
  6. Console Allocation: AllocConsole from non-console process
  7. Memory Patterns: .NET assemblies visible in process memory

Mitigation Strategies

  1. Limit Use: Only execute assemblies when necessary
  2. Target Legitimate Processes: Use process injection into existing .NET processes
  3. Clean Memory: Ensure proper cleanup after execution
  4. Avoid Common Tools: Don’t run Rubeus, SharpHound, etc. repeatedly

Usage Examples

Limitations

  1. Single CLR Version: Only one CLR version can be loaded per process
  2. AppDomain Isolation: Limited isolation between successive assembly executions
  3. Memory Footprint: CLR significantly increases process memory
  4. No Unloading: CLR cannot be fully unloaded once initialized
  5. x64 Only: Hardware breakpoint bypass only works on x64
  • Indirect Syscalls - Used for memory operations
  • COFF Loader - Alternative to .NET assemblies
  • Process injection for assembly execution in remote processes

References

  • CLR hosting: payloads/Demon/src/core/Dotnet.c
  • AMSI bypass: payloads/Demon/src/core/HwBpExceptions.c
  • Hardware breakpoints: payloads/Demon/src/core/HwBpEngine.c
  • Microsoft CLR Hosting documentation