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
Debuggervalue: 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_DLLsat0. - 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
.scrinSystem32, 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
- Load both hives. SOFTWARE carries IFEO, AppInit, AppCompatFlags, Netsh, and
HKLM\Software\Classes. SYSTEM carries AppCertDLLs. Per-user artifacts (Screensaver,HKCU\Software\ClassesCOM) live in everyNTUSER.DATandUsrClass.dat. A SOFTWARE-only sweep misses half of T1546. - Walk each key and subtract the baseline. Dump IFEO subkeys, AppInit values,
Custom/InstalledSDBentries, Netsh helpers,SCRNSAVE.EXE, and per-userInprocServer32registrations. Filter to entries that point outsideSystem32/Program Files, are unsigned, or namecmd/powershell/rundll32/regsvr32. - 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\CLSIDand intersect the GUIDs withHKLM\Software\Classes\CLSID. - 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.
- 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
LastWritetimes, plus transaction-log (.LOG1/.LOG2) residue, narrow it further. - Corroborate the trigger in EVTX. Each sub-technique has a characteristic trigger event —
netsh.exeexecution, 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
- Run keys and the IFEO debugger trick — the boot/logon autostart family (T1547) plus the full IFEO treatment.
- AppInit_DLLs and AppCertDLLs — the two DLL-injection vectors, hive by hive, and why one is mostly dead on modern Windows.
- Winlogon Notify, Shell, and Userinit hooks — logon-triggered code execution, adjacent to but distinct from event-triggered.
- MITRE ATT&CK and the registry — how the technique catalog maps onto the keys you actually parse.
- MITRE ATT&CK, T1546 Event Triggered Execution — the canonical sub-technique list and references.
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.