Nullpath

Windows Local Persistence

concept windowspersistencelocalred-team updated 07 Sept 2026 · 5 min

Windows Local Persistence

Staying on the box after you’ve won local access. Local privesc gets you admin/SYSTEM now; local persistence makes that access survive reboots, logoffs, and a cleanup pass, so the host remains a foothold you can re-use for credential-dumping, coercion, and lateral movement later. It’s the host-level sibling of ad-persistence (which is the domain-level DCShadow/Skeleton Key/AdminSDHolder game) — same intent, different object.

In an AD engagement the reason you persist locally is usually one of:

  • keep the pivot box alive (re-connect after a reboot / ticket expiry),
  • keep a coercion target warm (the box you run the printer bug / Potato from),
  • maintain local admin so the next time you’re on the box you’re already high-priv (no re-privesc).

The mechanisms

Run keys (logon-triggered)

Registry Run values execute at (user or system) logon:

HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Run            (all users)
HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Run            (per user)
HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce
HKLM\...\Winlogon  →  Userinit / Shell                          (logon shell)

A Run value pointing at your binary (or a rundll32.exe <yourdll>,<entry>) fires every logon. Quiet, but a logon is required — it doesn’t fire on a headless reboot with no logon.

Services (start-triggered, SYSTEM)

Install a service that runs your binary at startup as LOCAL SYSTEM: sc create evil binPath= C:\...\evil.exe start= auto. Fires on reboot (no logon needed) and runs at the top local privilege. The same object services are privesc’d through — here you’re creating one to persist. The tell is Event 7045 (new service).

Scheduled tasks (trigger of your choice)

A task that runs your binary at startup / logon / on a schedule. More flexible timing than a Run key (can fire without a logon, on a timer, at boot). Tell is Event 4698.

WMI (event-driven, the quiet one)

A WMI event subscription fires your command when a WMI event occurs — no new service, no Run key, no file in Startup. The three classes:

__EventFilter          (the trigger — e.g. "at startup", "on process start")
__CommandConsumer      (the action — your command)
__FilterToConsumerBinding   (glues filter → consumer)

Persist across reboot, run with the WMI service’s context (often SYSTEM), and leave no file in the usual persistence locations — which is exactly why defenders hunt them (Events 5857 / 5860 / 5861). The “quiet” persistence of choice when the EDR watches services + Run keys.

Startup folder (the simple one)

Drop a .lnk/.exe in the Startup folder (C:\Users\Public\Start Menu\Programs\Startup or the per-user one). Fires at logon. Trivially found by defenders — use it for speed, not stealth.

DLL side-load / COM hijack persistence

  • DLL side-load — leave a sidecar DLL next to a binary that runs at logon/boot; the trusted parent loads your DLL every time. Persistence and a trusted parent in one.
  • COM hijack — point a CLSID’s InprocServer32 at your DLL. A trusted binary that instantiates that COM object loads your DLL. Survives because it’s a registry value, not a file in an obvious spot.

Image File Execution Options (the debugger trap)

Set an IFEO Debugger value for a target executable (HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\<target>). Next time <target> runs, Windows launches your Debugger binary instead (alongside it). A persistence and a trigger-on-a-specific-app mechanism.

Choosing by objective

ObjectiveBest mechanismWhy
Survive reboot, SYSTEM, headlessService or WMIfire without a logon, top privilege
Quiet, no obvious fileWMI or COM hijackregistry/event, no Startup file, no 7045
Fast & simpleRun key / Startupone write, done (louder)
Trusted parentDLL side-loadthe parent is signed/known-good
Fire on a specific appIFEO Debuggertrigger tied to that app’s launch

Tooling

  • PowerUp (Add-ElevatedPersistence, Add-MITREAttack) — PowerShell automation for Run keys, services, and the rest.
  • sc / schtasks / reg — manual service / task / registry writes.
  • WMI via wmic / a WMI script — the subscription classes.

Red-team notes (OPSEC)

  • Match the persistence to the EDR — if the box’s EDR is screaming on new services (7045) and Run keys, go WMI or COM; if it’s young, a service is fine and easier to reason about.
  • One mechanism, not five — stacking Run key + service + task + WMI is insurance and four independent tells. Pick the one that fits the objective and the defender, and make it clean.
  • Keep it durable but reversible — you may need to remove the persistence during cleanup (or hand it over). Know exactly what you wrote so you can undo it without leaving a half-deleted service.
  • Persistence is a decision, not a default — on a box you’ll pivot off quickly, persistence may be overkill (more footprint, less gain). Persist where you’ll come back to; stay clean where you won’t.

Detection

  • Event 7045 — a new service (the service-persistence tell).
  • Event 4698 — a new scheduled task (the task tell).
  • Sysmon 13 — a write to a Run key / Winlogon / IFEO value (the registry tells).
  • Events 5857 / 5860 / 5861 — a WMI filter / consumer / binding created (the WMI tell — the quiet one defenders specifically hunt).
  • A new file in Startup / a new DLL beside a boot-time binary (the file-based tells).
  • The meta-signal — a persistence mechanism written by a non-admin or appearing minutes after a privesc event (the correlation that ties it to your foothold).