Registry Parser
All articles

Detecting T1546: event-triggered execution in the registry

8 min read

MITRE ATT&CK T1546, Event Triggered Execution, is the persistence family that does not fire at boot or at logon. It fires when something happens: a specific binary launches, a process spawns a child, the lock screen renders an accessibility tool, a network command runs, an idle timer trips, a COM object gets instantiated. The payload sits dormant in the registry until the triggering event occurs, and then it runs in whatever context that event provided — sometimes SYSTEM, sometimes the user, sometimes inside a process the attacker never had to start themselves. That indirection is the whole appeal, and it is why event-triggered persistence is harder to spot than a Run key. IFEO registry detection in particular is a recurring miss because the legitimate feature is real and the malicious use looks almost identical.

This post walks the registry-resident sub-techniques of T1546, gives the key path and the detection signal for each, then covers what normal looks like and how to hunt the set. For the autostart family that fires at boot/logon instead, see Run keys and the IFEO debugger trick; for where this all sits in the broader catalog, MITRE ATT&CK and the registry.

The registry sub-techniques

Image File Execution Options debugger (T1546.012)

HKLM\Software\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\<image>

A Debugger value under a subkey named after an executable replaces that executable: Windows launches the debugger with the original binary as an argument every time the original is invoked. The classic abuse is the sticky-keys backdoor — sethc.exe\Debugger set to cmd.exe, yielding a SYSTEM shell from the lock screen — but the same trick works against utilman.exe, osk.exe, narrator.exe, magnify.exe. The related SilentProcessExit key (...\CurrentVersion\SilentProcessExit\<image>, value MonitorProcess) launches a process when the named one exits, a quieter variant.

Detection signal. Any Debugger or MonitorProcess value at all is rare on a clean build. Flag anything pointing at cmd.exe, powershell.exe, an unsigned binary, or a path under \AppData\, \Temp\, or \ProgramData\. The deep-dive is in the Run keys and IFEO post.

AppInit DLLs (T1546.009)

HKLM\Software\Microsoft\Windows NT\CurrentVersion\Windows — values AppInit_DLLs, LoadAppInit_DLLs, RequireSignedAppInit_DLLs

A DLL named in AppInit_DLLs loads into every process that links user32.dll — effectively every interactive process — when LoadAppInit_DLLs is 1. One value, code in hundreds of process contexts.

Detection signal. Non-empty AppInit_DLLs with LoadAppInit_DLLs set to 1. Read both values together; a path with the flag at 0 is dormant. On Secure Boot / signing-enforced modern builds the mechanism is largely inert, so weigh the finding against the build. The companion AppCertDLLs mechanism (SYSTEM hive, fires on CreateProcess) is its less-neutered sibling. Full treatment: AppInit_DLLs and AppCertDLLs.

Application Shimming (T1546.011)

HKLM\Software\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Custom\ and ...\AppCompatFlags\InstalledSDB\

The Application Compatibility Toolkit lets administrators register Shim Database (.sdb) files that patch a target executable's behavior at load time. A malicious shim can inject a DLL, redirect execution, or disable security features, and it fires whenever the shimmed binary runs. InstalledSDB records the installed databases by GUID; Custom\<exe> maps an executable name to the shims applied to it.

Detection signal. Entries under Custom\ or InstalledSDB\ that you cannot tie to a known compatibility package, and any .sdb file living outside C:\Windows\AppPatch\ (the legitimate store). Cross-check the referenced .sdb on disk for signature and provenance. Custom shims are uncommon in most enterprises, so the population is small enough to enumerate by hand.

Netsh Helper DLL (T1546.007)

HKLM\Software\Microsoft\Netsh

Each value under this key names a helper DLL that netsh.exe loads every time it runs. Register a malicious DLL here and it executes whenever anything — an admin, a script, a scheduled task — invokes netsh.

Detection signal. The default helper list is short and vendor-known (dhcpcmonitor.dll, fwcfg.dll, wcmdll.dll, and similar). Any helper outside System32, any unsigned DLL, or any name you do not recognize is the finding. The trigger is netsh execution, so corroborate with process-creation telemetry for netsh.exe spawning unexpected child loads.

Accessibility features / sticky-keys hijack

Two mechanisms, one objective. The first is the IFEO Debugger trick against accessibility binaries, covered above. The second is plain binary replacement on disk (sethc.exe, utilman.exe) — not a registry artifact, but the registry-resident IFEO variant is the one you detect from a hive.

Detection signal. From the registry side, this collapses into the IFEO walk: any Debugger value under sethc.exe, utilman.exe, osk.exe, narrator.exe, magnify.exe, or displayswitch.exe. ATT&CK files binary-replacement under T1546.008; the registry footprint is purely the IFEO subkeys.

Screensaver (T1546.002)

HKCU\Control Panel\Desktop — values SCRNSAVE.EXE, ScreenSaveActive, ScreenSaverTimeOut, ScreenSaverIsSecure

SCRNSAVE.EXE names the .scr (a renamed PE) that runs when the idle timer in ScreenSaverTimeOut elapses, provided ScreenSaveActive is 1. The event trigger here is user inactivity.

Detection signal. A SCRNSAVE.EXE value pointing anywhere other than System32 (the stock screensavers — scrnsave.scr, Bubbles.scr, Mystify.scr, Ribbons.scr, PhotoScreensaver.scr, ssText3d.scr) is the tell. A .scr under \AppData\, \Temp\, or a user profile directory is event-triggered execution wearing a screensaver's clothes. Note this is per-user, so it lives in every NTUSER.DAT.

COM hijacking (T1546.015)

HKCU\Software\Classes\CLSID\{guid}\InprocServer32 (and LocalServer32, TreatAs)

COM resolves a class to its server DLL/EXE via the registry, and the per-user HKCU\Software\Classes view is searched before the machine-wide HKLM\Software\Classes. Write an InprocServer32 under HKCU for a CLSID that some routinely-loaded component resolves, and your DLL loads instead of the legitimate one — whenever that COM object is instantiated, which for well-chosen CLSIDs is constantly.

Detection signal. Any CLSID\{guid}\InprocServer32 present in HKCU\Software\Classes (the user hive's UsrClass.dat) that also exists in HKLM\Software\Classes is a shadowing hijack — the per-user entry overrides the system one. Pointing at a DLL outside System32/Program Files, or an unsigned DLL, confirms it. This is the subtle one; it requires diffing the per-user CLSID tree against the machine view. Treat HKCU COM registrations for system CLSIDs as suspicious by default.

What normal looks like

The unifying detection advantage across T1546 is that the legitimate population for most of these keys is tiny or empty:

  • IFEO subkeys with a Debugger value: essentially none on a default build. A handful of dev tools and AV products set them; the list is short enough to memorize.
  • AppInit_DLLs: present-but-empty on a clean install, with LoadAppInit_DLLs at 0.
  • AppCompatFlags\Custom / InstalledSDB: a few vendor compatibility packages at most; many hosts have none.
  • Netsh helpers: the fixed vendor list under HKLM\Software\Microsoft\Netsh.
  • SCRNSAVE.EXE: a stock .scr in System32, or unset.
  • HKCU CLSID registrations: legitimate per-user COM registration exists (some installers do it), but per-user shadowing of a CLSID that also lives in HKLM is unusual and worth scrutiny.

Because the baselines are so small, the hunt is mostly subtraction: enumerate the key, remove the known-good vendor entries, and look hard at whatever remains.

How to hunt them

  1. Load both hives. SOFTWARE carries IFEO, AppInit, AppCompatFlags, Netsh, and HKLM\Software\Classes. SYSTEM carries AppCertDLLs. Per-user artifacts (Screensaver, HKCU\Software\Classes COM) live in every NTUSER.DAT and UsrClass.dat. A SOFTWARE-only sweep misses half of T1546.
  2. Walk each key and subtract the baseline. Dump IFEO subkeys, AppInit values, Custom/InstalledSDB entries, Netsh helpers, SCRNSAVE.EXE, and per-user InprocServer32 registrations. Filter to entries that point outside System32 / Program Files, are unsigned, or name cmd/powershell/rundll32/regsvr32.
  3. Diff per-user COM against the machine view. For COM hijacking specifically, the signal is a CLSID present in both HKCU and HKLM. Enumerate HKCU\Software\Classes\CLSID and intersect the GUIDs with HKLM\Software\Classes\CLSID.
  4. Resolve every referenced binary. Each value is a lead, not a conclusion. Pull the DLL/EXE from the MFT, check Authenticode, look it up in AmCache, Shimcache, and Prefetch for first-execution timing.
  5. Diff against VSS snapshots. A T1546 value present in the live hive but absent two days ago is your incident with the timeline already bounded. Recently changed key LastWrite times, plus transaction-log (.LOG1/.LOG2) residue, narrow it further.
  6. Corroborate the trigger in EVTX. Each sub-technique has a characteristic trigger event — netsh.exe execution, the shimmed binary launching, the COM host process loading an unexpected module. Sysmon 1 (process create) and 7 (image load) tie the registry persistence to the moment it fired.

Investigate it in your browser

You can analyze the hive in your browser without standing up a forensic VM or a Perl environment. Drop in a SOFTWARE, SYSTEM, NTUSER.DAT, or UsrClass.dat hive and the parser surfaces the T1546 keys as persistence-category findings — IFEO Debugger values, AppInit/AppCert DLLs, Netsh helpers, AppCompatFlags shims, and the screensaver value — with notes on why each is flagged. Everything runs client-side; the hive never leaves your machine, which matters when the file is evidence.

Related techniques

The pattern to internalize: T1546 persistence does not announce itself at boot. It waits for an event you would never think to instrument, then runs in a context you did not grant it. The defense is the same for every sub-technique above — enumerate the key, subtract the known-good baseline, and resolve what is left. The baselines are small. The leftovers are where the incident lives.