>
av.Amit VijayanCYBERSECURITY & RESEARCHLet\u2019s connect
Back to the library

Threat Hunting Playbooks

Hunting COM Hijacking: Registry Persistence That Needs No Admin Rights

Thumbnail for: Hunting COM Hijacking: Registry Persistence That Needs No Admin Rights

Why this matters now

Give an attacker a standard user account — no admin rights, no UAC prompt, no service install — and they can still own a workstation across every reboot. One registry value under HKCU\Software\Classes\CLSID is enough: every time a trusted process asks Windows for that component, it gets the attacker's DLL or executable instead. MITRE tracks it as T1546.015 (Event Triggered Execution: COM Hijacking), and it keeps appearing in real intrusions because of one design fact defenders rarely internalize: per-user COM registration in HKCU takes precedence over machine-wide registration in HKLM. That precedence is a feature, not a bug — and it turns a low-privilege foothold into durable persistence.

It is also quiet by construction. There is no file in a startup folder, no scheduled task, no service — just a registry value the operating system honors by design. Most detection programs cover Run keys and services religiously and leave COM registration almost entirely unmonitored, which is exactly the gap this playbook closes.

The precedence trick

When a process wants a COM component, Windows resolves the class ID (CLSID) through the registry. The lookup checks HKCU\Software\Classes\CLSID\{guid} first and only falls back to HKLM\Software\Classes\CLSID\{guid} if nothing is found. A standard user cannot write to HKLM — but they own their entire HKCU hive, so they can register a shadow copy of any legitimate CLSID and win the resolution race.

Three registry subkeys do the damage:

  • InprocServer32 — the DLL Windows loads into the calling process. Hijack this and your payload runs inside a trusted host process.
  • LocalServer32 — the executable Windows launches as the COM server. Hijack this and your payload starts as its own process.
  • TreatAs — a redirection: Windows treats one CLSID as another. An attacker sets TreatAs on a widely-used class so legitimate requests are silently rerouted to the malicious component.

There is a fourth flavor worth knowing: phantom CLSID hijacking. Some applications request class IDs that no one registered — often from typos or dead dependencies. The attacker simply registers that CLSID in HKCU first, and the application's own lookup loads the payload. No legitimate key is even modified.

The practical consequence: the payload executes in the context of whatever trusted process requested the component — explorer.exe, a browser, an Office application. That is persistence and defense evasion in a single registry write, which is why COM hijacking frequently shows up in the early hours of an intrusion, long before privilege escalation.

The KQL hunts

These run in Microsoft Defender XDR advanced hunting. DeviceRegistryEvents records every registry value set and key created on onboarded devices, including the HKCU writes this technique depends on. Start by baselining: installers, updaters, browsers and Office legitimately register COM objects. What you are hunting is a registration whose target lives in a user-writable directory, or a registration written by a process that has no business installing software.

Hunt 1 — Rogue CLSID registrations pointing at user-writable paths

Legitimate COM servers live in System32, Program Files, and other protected locations. A payload staged for COM hijacking usually lives where the attacker could write without admin rights — the user profile, AppData, Temp, or ProgramData:

// Hunt: COM CLSID registrations redirecting to user-writable locations
DeviceRegistryEvents
| where Timestamp > ago(14d)
| where ActionType in ("RegistryValueSet", "RegistryKeyCreated")
| where RegistryKey has @"\CLSID\"
| where RegistryValueName in ("(Default)", "InprocServer32", "LocalServer32")
   or RegistryKey endswith @"\InprocServer32"
   or RegistryKey endswith @"\LocalServer32"
| where RegistryValueData has_any (@"\Users\", @"\AppData\", @"\Temp\", @"\ProgramData\")
| where not(RegistryValueData has @"\Windows\System32")
| project Timestamp, DeviceName, RegistryKey, RegistryValueName, RegistryValueData,
          InitiatingProcessAccountName, InitiatingProcessFileName, InitiatingProcessCommandLine
| order by Timestamp desc

Expect some benign hits from installers writing to ProgramData. The cases that matter are per-user writes (AppData\Roaming, AppData\Local\Temp) with no corresponding software installation — and anything whose target file does not exist or is unsigned.

Hunt 2 — TreatAs redirections

A TreatAs value on a CLSID is rare in legitimate software and classic in hijacking. Treat every one as suspicious until proven otherwise:

// Hunt: TreatAs class-ID redirections — COM hijacking by alias
DeviceRegistryEvents
| where Timestamp > ago(14d)
| where ActionType == "RegistryValueSet"
| where RegistryKey has @"\CLSID\"
| where RegistryValueName == "TreatAs"
| project Timestamp, DeviceName, RegistryKey, RegistryValueName, RegistryValueData,
          InitiatingProcessAccountName, InitiatingProcessFileName, InitiatingProcessCommandLine
| order by Timestamp desc

Then resolve both ends: look up the source CLSID (what the application requested) and the destination CLSID (what it actually got) in the same table. A destination that points back into a user-writable DLL is a completed hijack.

Hunt 3 — The installer: who wrote the registration

Before the registration exists, something wrote it. Attackers use reg.exe, PowerShell, or their own dropper. Catching the writer closes the gap when the payload file was already deleted:

// Hunt: processes writing COM CLSID registrations
DeviceProcessEvents
| where Timestamp > ago(14d)
| where ProcessCommandLine has_any (@"\Classes\CLSID", "InprocServer32", "LocalServer32", "TreatAs")
| where FileName in~ ("reg.exe", "regedit.exe", "powershell.exe", "pwsh.exe")
   or (FileName !in~ ("msiexec.exe", "setup.exe", "installer.exe"))
| project Timestamp, DeviceName, InitiatingProcessAccountName, InitiatingProcessFileName,
          FileName, ProcessCommandLine
| order by Timestamp desc

Note the carve-out: msiexec.exe and named installers register COM objects constantly. A reg.exe add ... \CLSID\ executed by a user-context process — or a PowerShell New-Item under HKCU:\Software\Classes\CLSID with no deployment tooling in the chain — is the shape of the attack.

Splunk equivalents

These assume Sysmon and Windows Security log data flowing. One honest caveat first: default Sysmon configurations log a curated set of registry paths, and HKCU\Software\Classes\CLSID is frequently not among them. If these queries return nothing across a fleet, that is a coverage finding, not a clean bill of health — add a RegistryEvent include for the CLSID tree before concluding anything.

SPL 1 — Sysmon 12/13: registry writes under the CLSID tree

Sysmon event 12 fires on registry key creation and deletion, event 13 on value sets — the two actions that build a hijack:

index=wineventlog SourceName="Microsoft-Windows-Sysmon" (EventCode=12 OR EventCode=13)
  TargetObject="*\\Classes\\CLSID\\*"
| eval action=case(EventCode=12, "key create/delete", EventCode=13, "value set")
| where Details="*\\Users\\*" OR Details="*\\AppData\\*" OR Details="*\\Temp\\*" OR Details="*\\ProgramData\\*"
| stats earliest(_time) AS first_seen, latest(_time) AS last_seen, count AS events,
    values(action) AS actions, values(ProcessId) AS pids, values(Image) AS writers by host
| sort - first_seen

SPL 2 — Security 4657: the native Windows audit record

Event 4657 (registry value modified) fires natively, but only for keys carrying a SACL — which CLSID does not have by default. If you configure SACLs on the user Classes tree, this becomes your Sysmon-independent source:

index=wineventlog EventCode=4657 ObjectName="*\\CLSID\\*"
| stats count AS events, values(NewValue) AS targets, values(ProcessName) AS writers by host, SubjectUserName
| sort - events

Triage: what to do when a hunt hits

  1. Check the target file. Does the DLL or EXE exist? Is it signed, and by whom? In Defender, DeviceFileCertificateInfo answers both; unsigned payloads in user-writable paths are the strongest single indicator of a live hijack.
  2. Compare HKCU against HKLM. Look up the same CLSID under HKLM\Software\Classes\CLSID. If the machine hive registers a legitimate path (a Microsoft or vendor DLL) and the user hive overrides it with a user-writable path, the hijack is confirmed — this is the precedence trick made visible.
  3. Enumerate with Autoruns. Sysinternals Autoruns has a dedicated view for COM/codec registrations; run it on the host and compare the flagged entries against a known-good baseline. It catches the phantom-CLSID flavor automatically.
  4. Follow the execution. Search DeviceProcessEvents for the payload's file name to find when the trusted host process last loaded it, and walk the process tree backward to the dropper or initial-access vector.
  5. Go enterprise-wide. Search the same registry pattern across all devices and the payload's hash across DeviceFileEvents. COM hijacking rarely appears on exactly one host.

Remediation and hardening

Remove the rogue HKCU registration and delete the payload file, then rotate the affected account's credentials — a hijacked COM object runs in the victim's context and may have harvested tokens or session material. Hunt the estate for the same CLSID and payload hash before closing the case; the per-user nature of this persistence means it is easy to leave a second copy on an adjacent host.

For hardening, two controls pay off disproportionately. First, application control: WDAC or AppLocker policies that block execution from user-writable directories neutralize the payload even if the registration is written, because the hijack's target can no longer run. Second, baseline the CLSID tree. Snapshot legitimate per-user COM registrations across the fleet and alert on new TreatAs values and InprocServer32 writes by non-installer processes — COM hijacking is rare enough in legitimate software that the alert volume stays manageable. And remember the forensic detail that makes this technique special: a HKCU hijack survives reboots but not profile deletion, and it leaves almost no trace outside the registry — so if your detection stack cannot see HKCU\Software\Classes\CLSID, add that coverage before the attacker who can read this article does.

Keep exploring.

All articles