Persistence via Run keys and the IFEO debugger trick
7 min read
There is a famous reference matrix listing somewhere north of a hundred persistence locations in the Windows registry. Memorizing it is not the goal. The goal is to triage the boring ones first, because nine times out of ten the attacker took the boring path.
This post is the workflow I run on every "is this host compromised" call before I let myself get clever.
Every Run key variant, in priority order
Start here. These are the keys that survive reboots, fire under the right user context, and require no privilege beyond writing to the key itself. They catch most opportunistic malware and a surprising amount of targeted intrusion work.
Machine-wide (SOFTWARE hive):
HKLM\Software\Microsoft\Windows\CurrentVersion\Run: fires for every user at logon, in that user's context. The most common persistence key on the planet.HKLM\Software\Microsoft\Windows\CurrentVersion\RunOnce: fires once at next logon then deletes. Common during install processes; persistent malware uses it less but not never.HKLM\Software\Microsoft\Windows\CurrentVersion\RunOnceEx: fires before Explorer, sequentially, can chain DLLs. Less common; when present, look hard.HKLM\Software\Microsoft\Windows\CurrentVersion\RunServicesandRunServicesOnce: legacy keys, ignored on modern Windows but still cargo-culted into malware that targets a broad range of hosts.HKLM\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Runand friends: the 32-bit-on-64-bit views of the same keys. Always check both. Malware that wants to hide will write to whichever view the standard checking tool ignores.
Per-user (NTUSER.DAT):
HKCU\Software\Microsoft\Windows\CurrentVersion\Run: fires at logon for that user. The most common per-user persistence.HKCU\Software\Microsoft\Windows\CurrentVersion\RunOnce: one-shot per-user.HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\Run: undocumented Run-equivalent, occasionally used.HKCU\Software\Microsoft\Windows NT\CurrentVersion\Windows\Load: per-user load value, rarely set in legitimate installs.HKCU\Software\Microsoft\Windows NT\CurrentVersion\Windows\Run: sibling ofLoad, also rare in legitimate use.
Triage rule: dump every value from every key above on every NTUSER.DAT on the host. Default content is well-known. Anything not matching a vendor pattern deserves a second look. RegRipper's run plugin walks this for you in one shot.
The IFEO debugger trick
HKLM\Software\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\ exists so developers can attach a debugger to an executable by name: create a subkey named after the target binary, add a Debugger value pointing at the debugger, and Windows launches the debugger with the original binary as the argument every time the original is invoked. Whatever is in the Debugger value runs in place of the original binary.
Three classic abuses:
Sticky-keys backdoor. Set Image File Execution Options\sethc.exe\Debugger to cmd.exe. When the user presses Shift five times at the lock screen or RDP login, Windows tries to launch sethc.exe and instead launches cmd.exe running as SYSTEM. From a lock screen. It is preposterous that this still works in 2026. It still works.
Utilman, OSK, Narrator, DisplaySwitch. Same trick, different accessibility binary. utilman.exe, osk.exe, magnify.exe, narrator.exe, displayswitch.exe. Defenders who only check sethc miss the rest.
Process kill. Set IFEO\<targetbinary>\Debugger to a non-existent path. Every time the target runs, it fails. Malware uses this to kill security products by exe name.
Walk every subkey. The list of legitimately-set IFEO subkeys on a default install is short. Anything pointing at cmd.exe, powershell.exe, an unsigned binary, or a path in \AppData\ or \Temp\ is malicious until proven otherwise. Also check SilentProcessExit (HKLM\Software\Microsoft\Windows NT\CurrentVersion\SilentProcessExit\<image>), which can launch another process when the named one exits.
Winlogon and the user-init chain
HKLM\Software\Microsoft\Windows NT\CurrentVersion\Winlogon is the second category of "boring but devastating" persistence:
Userinit: should beC:\Windows\system32\userinit.exe,. Anything appended after the comma fires at every interactive logon. Old persistence technique, still in active use.Shell: should beexplorer.exe. Replacing or appending to this fires at logon. If shell is replaced with a malicious binary, that binary is the shell. The user has no Explorer.Notify: pre-Vista notification packages. Should be empty on modern Windows.LegalNoticeCaptionandLegalNoticeText: not persistence, but attackers occasionally use them for taunting messages. Check anyway.
The same patterns apply at HKLM\Software\Wow6432Node\... for 32-bit views.
Services running from places they should not
Services are the dominant persistence vector on enterprise-targeted intrusions. The mechanics:
HKLM\System\CurrentControlSet\Services\<name> is one subkey per service. The values that matter:
ImagePath: the binary the service runs. Quoted or unquoted; relative or absolute; sometimes pointing atsvchost.exewith a-kgroup.ServiceDll: underParameters\, for svchost-hosted services, the DLL that gets loaded into the svchost process.Start: 2 is automatic, 3 is manual, 4 is disabled, 0 and 1 are boot-driver and system-driver.Type: 0x10 is own-process, 0x20 is shared-process, 0x1 is kernel driver.
Triage rule: dump ImagePath and ServiceDll for every service. Filter on paths outside the standard system directories. Anything in \AppData\, \Users\Public\, \Temp\, or C:\ProgramData\<random>\ is highly suspicious. RegRipper's svc and services plugins do this triage.
A persistent attacker trick: replace the ServiceDll of a real svchost service that nobody monitors with a malicious DLL. The service shows up in services.msc as the legitimate service. The binary running is the attacker's. Cross-validate ServiceDll against the known-good list, not against the display name.
Scheduled tasks: the registry-backed half
Scheduled tasks live in two places. The XML half is in C:\Windows\System32\Tasks\<TaskName>. The registry half is in HKLM\Software\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tasks\<GUID> and the parallel Tree\ and Plain\ subkeys.
Attackers occasionally manipulate one half without the other. The classic technique:
- Create a scheduled task normally.
- Delete the registry entry (or the XML).
- The half that remains may still cause the task to run, depending on the Windows version and how the Task Scheduler caches.
Two-sided detection: parse both halves and compare. Any task referenced in one half but not the other is suspicious. RegRipper does the registry side. The XML side is straightforward to walk.
Also: Microsoft\Windows NT\CurrentVersion\Schedule\CompatibilityAdapter\Signatures\ carries hashes of task XMLs. Mismatches between the recorded hash and the actual XML indicate tampering.
Startup folder redirection
HKLM\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders and User Shell Folders define where the Startup folder lives. The default points at C:\ProgramData\Microsoft\Windows\Start Menu\Programs\StartUp. If an attacker rewrites this to point at a writable directory under \AppData\ or C:\Users\Public\, every binary in that directory runs at logon. Rare in practice because it requires SOFTWARE write, but devastating when present and most autoruns checkers miss it. Same applies to Common Startup and per-user equivalents.
A detection workflow that works
For a host you suspect is compromised:
- Acquire all hives. SYSTEM, SOFTWARE, every NTUSER.DAT, every UsrClass.dat, plus
.LOG1and.LOG2for each. Include profiles that have not logged in recently. - Run autoruns offline. Sysinternals
autorunscan target an offline registry viaautorunsc.exe -eagainst a mounted hive. The output is verbose but the column-sort and the "verify signatures" option do the first pass for you. - Walk the Run keys manually. Even with autoruns, walk them with a tool that shows you the raw values. Autoruns hides things it considers benign.
- IFEO walk. Dump every subkey under
Image File Execution Options\. CheckDebugger,GlobalFlag, andMitigationOptionsvalues. - Services walk. Dump every
Services\<name>\ImagePathandServiceDll. Cross-check against the Windows known-services list. - Scheduled tasks walk. Parse both halves (registry and XML). Diff for inconsistencies.
- Diff against VSS snapshots. If any of the persistence keys above changed in the last day or week relative to a snapshot, that change is your lead.
- Pivot to file artifacts. For any binary referenced by a persistence value, look up its AmCache record, its Prefetch entry, its MFT timestamps, its Shimcache entry. The persistence key is the lead; the file artifacts are the corroboration.
- Pivot to EVTX. Process creation events (Security 4688 if enabled, Sysmon 1 if installed) corroborate when the persistence fired.
VSS diff is the single highest-value step. Persistence that appears in the live hive but not in a snapshot from two days ago is your incident, with the timestamp range narrowed to between the snapshots.
What attackers do, and what survives
The savvy attacker writes a Run value, runs the payload, deletes the value. The key's LastWrite reflects the deletion. The previous value lives in the transaction logs if the deletion was recent, in VSS snapshots if those were running, and in unallocated cells of the hive itself, recoverable via yarp or RegRipper's del plugin. Attackers who shrink the hive with reg unload and force a flush can sometimes purge those cells. Most do not bother.
Further reading
- Microsoft, Image File Execution Options: the vendor documentation for the legitimate feature.
- Sysinternals, Autoruns: the tool every analyst eventually relies on.
- MITRE ATT&CK, Boot or Logon Autostart Execution: the technique catalog with the relevant subtechniques mapped.
- Harlan Carvey, Windows Incident Response blog: search for "persistence" for years of case notes.
Persistence detection is a checklist exercise more than a creative one. Run the checklist, find the boring stuff, then go look for the clever stuff. The boring stuff is what catches 90% of incidents.