← All posts

AppDomainManager Injection: The .NET Hijack That Runs Your Code Inside Trusted Managed Binaries

How attackers abuse the CLR's AppDomainManager to hijack signed .NET applications for stealthy execution and persistence on Windows, and what to audit.

Most defenders spend their energy hunting native code injection — VirtualAllocEx, reflective loaders, process hollowing. But there is a quieter path that requires no shellcode, no memory patching, and no unsigned binary hitting disk. It abuses a legitimate feature of the .NET Common Language Runtime (CLR): the AppDomainManager. With three configuration knobs, an attacker can make a fully signed, Microsoft-shipped .NET executable load and run their managed assembly at startup — inside a process that your allowlist, your EDR, and your analyst all trust.

How AppDomainManager injection works

When any .NET Framework application starts, the CLR checks whether a custom AppDomainManager should manage the default application domain. It resolves this from three environment variables or from the application's config file:

The equivalent config-file form lives in the app's *.exe.config:

<configuration>
  <runtime>
    <appDomainManagerAssembly value="Evil, Version=1.0.0.0,
        Culture=neutral, PublicKeyToken=abc123..." />
    <appDomainManagerType value="Evil.Hijack" />
  </runtime>
</configuration>

The attacker's class simply overrides InitializeNewDomain. That method runs before Main(), so the payload fires the instant the trusted host process launches:

public sealed class Hijack : AppDomainManager {
    public override void InitializeNewDomain(AppDomainSetup info) {
        // runs inside the signed host, before its Main()
        Payload.Run();
    }
}

Because the malicious code executes within a legitimate signed process (a common example is a small .NET utility that lives in a writable directory), the parent-child telemetry looks benign and the loaded module is a normal managed assembly. This maps to MITRE ATT&CK T1574.014 (Hijack Execution Flow: AppDomainManager).

Why it evades common controls

The hijack is not a bug — it is documented CLR behavior. That is exactly why it is durable: you cannot patch it away, you can only audit and constrain it.

What to hunt for

There is no single "off switch," so detection is about surfacing the three ingredients:

  1. Suspicious environment variables. Enumerate machine- and user-scope variables for APPDOMAIN_MANAGER_ASM, APPDOMAIN_MANAGER_TYPE, and COMPLUS_Version. These should essentially never appear on a normal endpoint.
  2. Rogue .exe.config files. Look for config files next to signed binaries that contain appDomainManagerAssembly / appDomainManagerType elements, especially in user-writable paths like %APPDATA%, %TEMP%, or C:\ProgramData.
  3. Unexpected managed modules. A signed .NET host loading an unsigned or oddly-located assembly from a temp directory is a strong signal. Sysmon Event ID 7 (Image Loaded) and CLR ETW provider events help here.

A quick manual sweep from PowerShell:

Get-ChildItem Env: | Where-Object {
  $_.Name -match 'APPDOMAIN_MANAGER|COMPLUS_Version'
}

[Environment]::GetEnvironmentVariables('Machine').GetEnumerator() |
  Where-Object { $_.Key -match 'APPDOMAIN_MANAGER' }

Hardening the surface

Where WinSentinel fits

AppDomainManager injection is precisely the kind of misconfiguration that hides between "the file is signed" and "the behavior is wrong." WinSentinel audits the endpoint's own configuration — environment variables, autostart and execution-flow hijack points, and file/permission hygiene — and flags the specific artifacts this technique depends on, so a planted config or a rogue variable shows up as a finding instead of living silently until an incident. Every WinSentinel install runs the full audit engine and all 64 modules locally on a single machine with no limits, so an individual admin can sweep their own workstation for this today at no cost.

For teams, WinSentinel Pro adds fleet orchestration: a central node rolls those same findings up across every managed machine, so if one endpoint suddenly grows an APPDOMAIN_MANAGER_ASM variable or a suspicious .exe.config, you see the drift across the fleet and can dispatch a remediation instead of discovering it one host at a time. Execution-flow hijacks are quiet by design — the win comes from auditing continuously and catching the configuration change the moment it lands.