# R3 — COM for Local Privilege Escalation (research brief)

**Scope:** Local privilege escalation via COM/DCOM only.
**Important correction (verified):** The DCOM/DCE-RPC local NTLM reflection bug is **CVE-2015-2370 (MS15-076, Project Zero issue 325)**. **CVE-2017-0100 (MS17-012) is a different DCOM LPE — the HelpPane.exe interactive-user client-verification bug** from Forshaw's COM Session Moniker research. Do not conflate.

---

## 1. WHY OUT-OF-PROCESS COM SERVERS ARE LPE TARGETS

### 1.1 The structural problem

- COM objects are activated through the SCM/**RPCSS** service. An **out-of-process (OOP) server** is registered via `HKLM\SOFTWARE\Classes\CLSID\{CLSID}` with `LocalServer32` or with an `AppID` that names a Windows service. The server process frequently runs as `NT AUTHORITY\SYSTEM`, `LOCAL SERVICE`, or `NETWORK SERVICE`.
- Whether a low-privileged user may even talk to such an object is governed by two SDDL strings stored under `HKLM\SOFTWARE\Classes\AppID\{AppID}`:
  - **`LaunchPermission`** — who may activate (rights mask includes `ExecuteLocal 0x2`, `ActivateLocal 0x8`).
  - **`AccessPermission`** — who may call methods on a running object.
  - Machine-wide defaults: `HKLM\SOFTWARE\Microsoft\Ole\DefaultLaunchPermission` / `DefaultAccessPermission` (editable via DCOMCNFG).
- Many inbox services grant launch+access to `Authenticated Users` (S-1-5-11), `INTERACTIVE` (S-1-5-4), or even AppContainer/LPAC principals. Example (DiagHub): launch SDDL contains `(A;;CCDCSW;;;AU)` — Authenticated Users — plus ALL APPLICATION PACKAGES and an LPAC capability SID. **Any such SYSTEM object with a dangerous method is a candidate LPE primitive.**
- The client-server call path is: `Client → Proxy → COM library/RPC runtime → ALPC or TCP/NP transport → Stub → Server object`. Marshalling is NDR (type-less "stubless" proxies) or type-library driven (`oleaut32`). All security context transfer relies on the impersonation contract below.

### 1.2 The impersonation contract (precision)

**Client side** — two levels:

1. Process-wide, once: `CoInitializeSecurity(pSecDesc, cAuthSvc, asAuthSvc, pReserved1, dwAuthnLevel, dwImpLevel, pAuthList, dwCapabilities, pReserved3)`.
2. Per-proxy (what matters for exploitation): `CoSetProxyBlanket(pProxy, dwAuthnSvc, dwAuthzSvc, pServerPrincName, dwAuthnLevel, dwImpLevel, pAuthInfo, dwCapabilities)` — a wrapper for `IClientSecurity::SetBlanket`.

Key constants (verified):

| Constant | Value | Meaning |
|---|---|---|
| `RPC_C_IMP_LEVEL_DEFAULT` | 0 | |
| `RPC_C_IMP_LEVEL_ANONYMOUS` | 1 | token unusable |
| `RPC_C_IMP_LEVEL_IDENTIFY` | 2 | server can identify client only — **token cannot be used to access objects** |
| `RPC_C_IMP_LEVEL_IMPERSONATE` | 3 | server may act as client **locally** |
| `RPC_C_IMP_LEVEL_DELEGATE` | 4 | act as client off-machine (Kerberos only) |
| `EOAC_STATIC_CLOAKING` | 0x20 | proxy uses the token captured at blanket-set time |
| `EOAC_DYNAMIC_CLOAKING` | 0x40 | proxy uses the *current thread* token per call |

Auth levels: `RPC_C_AUTHN_LEVEL_CONNECT=2` … `PKT_INTEGRITY=5`, `PKT_PRIVACY=6` (matters for the KB5004442 / CVE-2021-26414 DCOM hardening).

**Server side** — the correct pattern:

```cpp
DWORD impLevel; DWORD authnSvc, authzSvc, authnLevel, caps; RPC_AUTHZ_HANDLE privs;
CoQueryClientBlanket(&authnSvc, &authzSvc, NULL, &authnLevel, &impLevel, &privs, &caps);
if (impLevel < RPC_C_IMP_LEVEL_IMPERSONATE) return E_ACCESSDENIED;   // often missing!
HRESULT hr = CoImpersonateClient();   // thread token := client's token
/* ... privileged operation performed AS THE CLIENT ... */
CoRevertToSelf();
```

Transport equivalents: RPC servers use `RpcImpersonateClient()`/`RpcRevertToSelf()`; named-pipe servers use `ImpersonateNamedPipeClient()`/`RevertToSelf()`; kernel-side equivalent is `PsImpersonateClient` gated by `SeTokenCanImpersonate`.

### 1.3 How the contract fails (the LPE bug taxonomy)

1. **No impersonation at all** — server does file/registry/process ops as SYSTEM on attacker-controlled paths (the classic privileged-file-operation bug class).
2. **Late/short-lived impersonation** — server impersonates, checks access, then `RevertToSelf()` and performs the write as SYSTEM (the StorSvc `SvcMoveFileInheritSecurity` bug used in the DiagHub chain).
3. **Identification-token confusion** — server gets only an IDENTIFY token; use of it fails with error 1346 `ERROR_BAD_IMPERSONATION_LEVEL`. This is why early RoguePotato experiments got "useless" tokens and why Microsoft's CLSID hardening downgraded tokens to Identification.
4. **Cloaking confusion** — static vs dynamic cloaking picks the wrong token when the server itself makes outbound calls (relevant to relay/reflection chains).
5. **Cross-session / "interactive user" activation** — AppID `RunAs = Interactive User` objects instantiated from session 0 or another user's session (Session Moniker chain, CVE-2017-0100).
6. **Marshalled-by-reference leaks** — server returns live objects that were never meant to cross the boundary (trapped objects).

---

## 2. CASE STUDY — DIAGHUB (Forshaw, Project Zero, April 2018)

**Source:** "Windows Exploitation Tricks: Exploiting Arbitrary File Writes for Local Elevation of Privilege" — googleprojectzero.blogspot.com/2018/04/windows-exploitation-tricks-exploiting.html (mirror: projectzero.google). Companion bugs: P0 issues 1427 & 1428.

### 2.1 The service

- **Microsoft (R) Diagnostics Hub Standard Collector Service** ("DiagHub"), introduced in Windows 10 (Win7/8.1 analog: "IE ETW Collector"). Collects ETW diagnostics on behalf of sandboxed apps (Edge/IE). Runs as **SYSTEM**. Ships in `DiagnosticsHub.StandardCollector.Service.exe` + `DiagnosticsHub.StandardCollector.Runtime.dll`.
- History of abuse: previously exploited by **Lokihardt** (sandbox escape) and Forshaw (user→SYSTEM). Later CVEs kept landing: **CVE-2018-0952** (Atredis Partners PoC, github.com/atredispartners/CVE-2018-0952-SystemCollector), and a later case-sensitivity directory-traversal EoP (irsl PoC, github.com/irsl/microsoft-diaghub-case-sensitivity-eop-cve).

### 2.2 The DCOM object (exact IDs)

- **CLSID: `{42CBFAA7-A4A7-47BB-B422-BD10E9D02700}`** ("Diagnostics Hub Standard Collector Service"), DCOM-activatable by normal users *and* AppContainer/LPAC sandboxes.
- Interfaces: `IMarshal`, `IUnknown`, `IStandardCollectorAuthorizationService` (IID `2D2AC45D-03BB-4B8A-8EFE-93EF98217054`), **`IStandardCollectorService` (IID `0D8AF6B7-EFD5-4F6D-A834-314740AB8CAA`)**, proxy CLSID `4323664B-B884-4929-8377-D2FD097F7BD3`.
- `IStandardCollectorService` has 8 methods (3 `IUnknown` + 5): `CreateSession`, `GetSession`, `DestroySession`, `DestroySessionAsync`, `AddLifetimeMonitorProcessIdForSession` (names recovered via OleViewDotNet IPID→VTable workflow + WinDbg `dqs DiagnosticsHub_StandardCollector_Runtime+0x36C78 L8`; signatures via OVDN "View Proxy Definition" NDR decompilation).

### 2.3 The load-anything primitive

`ICollectionSession::AddAgent(dllName, guid)` — simplified server code:

```cpp
void EtwCollectionSession::AddAgent(LPWCSTR dll_path, REFGUID guid) {
    WCHAR valid_path[MAX_PATH];
    if (!GetValidAgentPath(dll_path, valid_path)) return E_INVALID_AGENT_PATH;
    HMODULE mod = LoadLibraryExW(valid_path, nullptr, LOAD_WITH_ALTERED_SEARCH_PATH);
    dll_get_class_obj = GetProcAddress(hModule, "DllGetClassObject");
    return dll_get_class_obj(guid);
}
```

`GetValidAgentPath` constrains the load to **C:\Windows\System32 but not to any extension**. So: write any file (e.g. `license.rtf`) whose contents are a valid DLL exporting `DllGetClassObject` into System32 → call `CreateSession` then `AddAgent(L"license.rtf", guid)` → code runs **as SYSTEM, outside DllMain/loader lock**. Client skeleton (from the blog):

```cpp
CoCreateInstance(CLSID_CollectorService, nullptr, CLSCTX_LOCAL_SERVER, IID_PPV_ARGS(&service));
SessionConfiguration config = {}; config.version = 1;
config.monitor_pid = GetCurrentProcessId(); CoCreateGuid(&config.guid);
config.path = SysAllocString(L"C:\\Dummy");
service->CreateSession(&config, nullptr, &session);
session->AddAgent(L"dummy.dll", agent_guid);
```

### 2.4 Full exploit chain (issue 1428 — arbitrary file write → SYSTEM)

1. Storage Service (`StorSvc`) RPC method `SvcMoveFileInheritSecurity` impersonates the caller, `MoveFileEx()`s a file, **then reverts impersonation** and calls `SetNamedSecurityInfo()` as SYSTEM to apply inherited ACEs. Two bugs: failed-ACL revert allows arbitrary file creation (issue 1427); and via **hard links**, `SetNamedSecurityInfo` applies attacker-chosen ACEs to *any* file SYSTEM can `WRITE_DAC` (issue 1428).
2. Pick a non-TrustedInstaller-owned, non-critical System32 file — the blog uses **`C:\Windows\System32\license.rtf`** (enumerate candidates with NtObjectManager's `Get-AccessibleFile -AccessRights WriteDac` run against the StorSvc process token).
3. Overwrite `license.rtf` with a DLL implementing `DllGetClassObject`.
4. DiagHub `CreateSession` → `AddAgent("license.rtf", …)` → **DLL loaded as SYSTEM**.
5. (Same primitive was later used by the Metasploit module for **CVE-2019-0841** AppXSvc hardlink EoP, which credits "James Forshaw — Code creating hard links and communicating with DiagHub service".)

### 2.5 Microsoft's mitigation

- **Windows 10 19H1 / 1903**: DiagHub hardened with a **`ProcessImageLoadPolicy`** restricting the collector to loading **Microsoft-signed binaries only** (first flagged publicly by @x9090 and @decoder_it on Twitter, Jan/Feb 2019). Killed the "load arbitrary extension from System32" trick. The USO trick was its replacement — then that was hardened too (2020-era Insider builds).
- Lesson: MS increasingly kills the *weaponization primitive* (loader policy, signing-level checks, KnownDlls) rather than each file-write bug — see also the oleaut32 `VerifyTrust` signing-level check.

---

## 3. CASE STUDY — USO / UPDATE ORCHESTRATOR (itm4n, Aug 2019)

**Sources:** "Weaponizing Privileged File Writes with the USO Service — Part 1" (itm4n.github.io/usodllloader-part1/, 2019-08-17) and "Part 2" (itm4n.github.io/usodllloader-part2/, 2019-08-19). Tool: github.com/itm4n/UsoDllLoader.

### 3.1 The primitive

- **Update Session Orchestrator** service (`UsoSvc`, `C:\Windows\System32\usosvc.dll`, runs as SYSTEM) attempts to load a **non-existent DLL `windowscoredeviceinfo.dll` from System32 every time an Update Session is created**.
- Any privileged-file-write primitive → drop payload as `C:\Windows\System32\windowscoredeviceinfo.dll` → trigger a scan via the built-in undocumented client `usoclient.exe StartScan` (or directly via COM) → code as SYSTEM **outside DllMain** (the service calls a specific export after load; also avoids loader-lock constraints).
- Not a vulnerability by itself — a **weaponization bridge**, the successor to DiagHub after 1903. itm4n's repo notes (2020-06): trick broken on then-current Insider builds; still worked on mainstream Win10 at that time; later patched.

### 3.2 The COM interface map (exact IDs)

- **CLSID `UpdateSessionOrchestrator` = `{B91D5831-B1BD-4608-8198-D72E155020F7}`**; AppID `{E7299E79-75E5-47BB-A03D-6D319FB7F886}`; server `usosvc.dll`.
- `CoCreateInstance` requests IID **`IUpdateSessionOrchestrator` = `{07F3AFAC-7C8A-4CE7-A5E0-3D24EE8A77E0}`**; its 5 methods past `IUnknown`: `CreateUpdateSession`, `GetCurrentActiveUpdateSessions`, `LogTaskRunning`, `CreateUxUpdateManager`, `CreateUniversalOrchestrator` (recovered from `usosvc.dll` VTable).
- **`IUsoSessionCommon` IID = `{FCCC288D-B47E-41FA-970C-935EC952F4A4}`** — 68 methods; OleViewDotNet auto-generated the interface code. Another GUID seen in a `QueryInterface`: `C57692F8-8F5F-47CB-9381-34329B40285A`.
- Reconstructed "StartScan" client flow (verbatim from part 2):

```cpp
CoInitializeEx(0, COINIT_MULTITHREADED);
CoCreateInstance(CLSID_UpdateSessionOrchestrator, nullptr, CLSCTX_LOCAL_SERVER,
                 IID_PPV_ARGS(&updateSessionOrchestrator));
updateSessionOrchestrator->LogTaskRunning(L"StartScan");
updateSessionOrchestrator->CreateUpdateSession(1, &IID_IUsoSessionCommon, &usoSessionCommon);
CoSetProxyBlanket(usoSessionCommon, RPC_C_AUTHN_DEFAULT, RPC_C_AUTHZ_DEFAULT,
                  COLE_DEFAULT_PRINCIPAL, RPC_C_AUTHN_LEVEL_DEFAULT,
                  RPC_C_IMP_LEVEL_IMPERSONATE, nullptr, NULL);
usoSessionCommon->Proc21(0, 0, L"ScanTriggerUsoClient");   // VTable[21] = StartScan trigger
CoUninitialize();
```

### 3.3 RE methodology demonstrated

Static: `symchk` for PDBs → string xref `"StartScan"` → `PerformOperationOnSession()`/`PerformOperationOnManager()`. Dynamic: breakpoint on `_guard_dispatch_icall_fptr` after `CoCreateInstance` → WinDbg `dqs` on proxy VTable (`usoapi!IUpdateSessionOrchestratorProxyVtbl`) → match `ObjectStublessClientN` slots to server-side symbols in `usosvc.dll`. OleViewDotNet failed to expand the object (`ClassFactory cannot supply requested class`) — worked around by client-side dynamic analysis; a realistic "tool fails, pivot" moment. **Note the `CoSetProxyBlanket(... RPC_C_IMP_LEVEL_IMPERSONATE ...)` call** — the client must *offer* impersonation; this is the contract in the wild.

---

## 4. DCOM NTLM REFLECTION — THE BUG CLASS, THE REAL CVE, AND THE HARDENING TIMELINE

### 4.1 The root bug: CVE-2015-2370 (MS15-076, July 14 2015) — Project Zero issue 325

- Reported by James Forshaw 2015-04-09: **"Windows: DCOM DCE/RPC Local NTLM Reflection Elevation of Privilege"** (bugs.chromium.org/p/project-zero/issues/detail?id=325; follow-on to unfixed WebDAV NTLM reflection issue 128).
- Mechanism: when a DCOM object reference is marshalled by reference (`OBJREF_STANDARD`), the OBJREF carries RPC **string bindings** (TowerID + address string). Specifying TowerID `NCACN_IP_TCP` and a string `host[port]` makes the **object resolver (rpcss, acting for the privileged server) open a TCP connection to an arbitrary local port** and perform NTLM authentication per the security bindings. A local attacker relays that SYSTEM NTLM exchange back into a *local* `AcceptSecurityContext()` loop → negotiates a **SYSTEM impersonation token locally** (or, in Forshaw's original PoC, reflects the auth to the local DCOM activation service to activate **`CLSID_Package {F20DA720-C02F-11CE-927B-0800095AE340}`** as SYSTEM; a variant wrote a file to `C:\Windows (2)`).
- Fix: MS15-076 — "the authentication implementation in the RPC subsystem … does not prevent DCE/RPC connection reflection" — Microsoft added reflection protections in the RPC runtime (`rpcrt4.dll`).
- Weaponization history: NetSPI's "Trebuchet" (blog: "Exploiting MS15-076 (CVE-2015-2370)") turned the PoC into arbitrary file write using Forshaw's SyScan'15 symlink/junction tricks (junction + `\RPC Control` object-manager symlinks via CreateSymlink). **This bug is the direct ancestor of every potato.**

### 4.2 CVE-2017-0100 (MS17-012, March 2017) — what it actually is

- Official title: **"Windows HelpPane Elevation of Privilege Vulnerability"** — "a DCOM object in **Helppane.exe**, configured to run as the interactive user, fails to properly authenticate the client." Local users gain privileges via a crafted application (Windows 7 SP1 → 10 1607, Server 2008 R2 → 2016).
- Context: part of Forshaw's **COM Session Moniker** EoP research (session-moniker activation of an interactive-user DCOM class from another session; Helppane then executed attacker content with insufficient client verification). Microsoft's fix *checked the wrong thing* — Forshaw's follow-up report **"Windows: Bad Fix for COM Session Moniker EoP"** became **CVE-2017-0298** (the HelpPane check compared integrity levels rather than verifying admin group; IL is not a security boundary; UIAccess IL-ratcheting could defeat it). Public text: exploit-db/0day.today ID 28134.
- Teaching point: verify *who the caller is* (client blanket/identity) not *how elevated their token looks*.

### 4.3 Microsoft's DCOM hardening timeline

| Date | Change | Impact |
|---|---|---|
| Jul 2015 | **MS15-076 / CVE-2015-2370** — RPC runtime blocks DCE/RPC connection reflection | Kills raw reflection; potato family moves to local token negotiation |
| Mar 2017 | **MS17-012 / CVE-2017-0100** (then CVE-2017-0298 bad-fix fix) | Session Moniker chain closed |
| May 2017 | **CVE-2017-0213** (COM Aggregate Marshaler, `CStdMarshal::Finish_RemQIAndUnmarshal2` RQI2 unpack bug) & **CVE-2017-0214** (type-library load validation) patched — both Forshaw; PoC: BITS `SetNotifyInterface` → load attacker type lib under impersonation → **scriptlet moniker** → code exec in BITS as SYSTEM. 0213 later entered CISA KEV | Type-library attack surface shrinks |
| Oct 2018 (RS5) | **Silent rpcss fix (Win10 1809 / Server 2019)**: OXID binding no longer built via string concat `host[port]`; new `CreateRemoteBindingToOR()` builds bindings via `RpcStringBindingCompose()` → `host[port][135]` fails parsing; also remote OXID queries forced to **port 135 only** and processed as **ANONYMOUS LOGON**; abusable-CLSID set hardened so captured tokens become **Identification** tokens; PrintNotify-style CLSIDs gated to **INTERACTIVE** group | Kills Rotten/JuicyPotato (decoder's post "No more rotten/juicy potato?", 2018-10-29) |
| 2019 (19H1/1903) | DiagHub **ProcessImageLoadPolicy** (MS-signed loads only) | Kills DiagHub weaponization |
| Jun 2021+ | **KB5004442 / CVE-2021-26414** — DCOM server hardening: minimum `RPC_C_AUTHN_LEVEL_PKT_INTEGRITY` enforced for remote DCOM activation (phased) | Mostly a *remote* DCOM mitigation |
| Jan 2023 | **CVE-2023-21746** — NTLM local-auth context-swap fix in `msv1_0.dll` (`SsprHandleChallengeMessage`: if `ISC_REQ_UNVERIFIED_TARGET_NAME` set and SPN present → SPN forced NULL → SMB anti-reflection on `cifs/127.0.0.1` fires) | Kills LocalPotato SMB scenario |

---

## 5. THE POTATO FAMILY — MECHANISMS, REQUIREMENTS, BUILD RANGES, AND *EXACTLY WHAT IS COM*

**Common prerequisites (all potatoes):** attacker code runs as an account holding **`SeImpersonatePrivilege`** and/or **`SeAssignPrimaryTokenPrivilege`** (IIS AppPool, MSSQL service, LOCAL/NETWORK SERVICE, scheduled-task service accounts). Token type math: `CreateProcessWithTokenW` (needs impersonate-capable primary token dup), `CreateProcessAsUser` (needs SeAssignPrimaryTokenPrivilege). **COM appears in this family in exactly three roles: (a) the *trigger* — DCOM activation (`CoGetInstanceFromIStorage` with a crafted `IStorage`) that forces a privileged COM server to authenticate to the attacker; (b) the *OXID resolver* — rpcss's `IObjectExporter::ResolveOxid2` interface (UUID `99FCFEC4-5260-101B-BBCB-00AA0021347A`) that locates objects; (c) the *callback surface* — attacker-implemented COM objects (e.g. fake `IUnknown`/`IRemUnknown2` server) into which the privileged side calls back, enabling `CoImpersonateClient()`/`RpcImpersonateClient()`. Everything else — NTLM relay, named-pipe impersonation, MS-RPRN/MS-EFSR coercion, SMB context swapping — is RPC/SMB/SSPI, not COM.**

### 5.0 Pre-history (one line each)

- **Hot Potato** (FoxGlove, Jan 2016): NBNS spoofing + WPAD + Windows Update HTTP NTLM → SYSTEM; MS16-075 (Metasploit `windows/local/ms16_075_reflection`). No COM.
- **RottenPotatoNG** (FoxGlove): official C++ port of Rotten without Meterpreter/Incognito dependency.

### 5.1 RottenPotato (FoxGlove Security — @breenmachine & @vvalien1, Sep 2016)

- **COM part:** `CoGetInstanceFromIStorage(NULL, CLSID_BITS, …)` with hardcoded **BITS CLSID `{4991D34B-80A1-4291-83B6-3328366B9097}`** and a hand-crafted `IStorage` whose string bindings point at `127.0.0.1:6666` → forces the BITS COM server (SYSTEM) to speak DCOM/RPC to the attacker's TCP listener.
- **Non-COM part:** byte-level relay of the RPC conversation to the real OXID resolver on TCP 135; when the NTLMSSP exchange begins, MITM swaps the Type 2 **challenge + "Reserved" field** (the Reserved field is a reference to the local SecHandle — *the* critical trick) with output of local `AcquireCredentialsHandle` + `AcceptSecurityContext()`; SYSTEM's Type 3 completes the *local* authentication → `ImpersonateSecurityContext()` → SYSTEM impersonation token → Meterpreter Incognito to use it.
- **Works:** ≤ Windows 10 **1803** / Server ≤ 2016 era (killed by the 1809 OXID changes). 100% reliable, instant, cross-version at the time.
- Refs: foxglovesecurity.com/2016/09/26/rotten-potato-privilege-escalation-from-service-accounts-to-system/; github.com/foxglovesec/RottenPotato.

### 5.2 JuicyPotato (ohpe/@Giutro & @decoder_it, Aug 2018)

- "RottenPotatoNG on steroids": **arbitrary CLSID** (`-c`), arbitrary local COM-listen port (`-l`), arbitrary program (`-p`), `-t *` tries both `CreateProcessWithTokenW` and `CreateProcessAsUser`. No Meterpreter.
- **Key research:** brute-forced/enumerated abusable CLSIDs — a usable CLSID must (1) be instantiable by the current (service) user, (2) implement **`IMarshal`**, (3) run as an elevated account. Per-OS CLSID lists published: ohpe.it/juicy-potato/CLSID/ (e.g., BITS `{4991D34B…}`, wuauserv/aucore {e60687f7-01a1-40aa-86ac-db1cbf673334} etc.). DiagHub's own CLSID `{42CBFAA7-A4A7-47BB-B422-BD10E9D02700}` appears in the Server 2016 list.
- **Works:** up to Win10 **1803** / Server 2016; broken by 1809/2019 silent fix.
- Refs: ohpe.it/juicy-potato/; github.com/ohpe/juicy-potato; decoder.cloud/2018/10/29/no-more-rotten-juicy-potato/.

### 5.3 RogueWinRM (side quest; part of SweetPotato)

BITS, when started, attempts HTTP NTLM auth to `127.0.0.1:5985` (WinRM) — if WinRM is off (default on clients), an unprivileged user can bind 5985 and capture SYSTEM HTTP NTLM → token. github.com/antonioCoco/RogueWinRM. Not COM itself (HTTP), but BITS trigger is the same service family. Ref: decoder.cloud "From NETWORK SERVICE to SYSTEM" + RogueWinRM repo.

### 5.4 RoguePotato (@decoder_it & @splinter_code, May 2020)

The comeback after the 1809 kills. Verified from the primary source (decoder.cloud/2020/05/11/no-more-juicypotato-old-story-welcome-roguepotato/):

1. **COM trigger (COM):** `CoGetInstanceFromIStorage` with BITS CLSID `{4991d34b-80a1-4291-83b6-3328366b9097}` (default; `-c` to override), `IStorage` string bindings pointing at a **remote attacker IP** — because post-1809 the OXID resolver is only reachable on **port 135** and remote OXID queries authenticate as **ANONYMOUS LOGON** (useless directly).
2. **Redirect:** attacker box runs `socat tcp-listen:135,reuseaddr,fork tcp:<victim>:9999` → forwards to the fake **RogueOxidResolver** RPC server (can run on victim on port 9999, or standalone on attacker Windows box).
3. **Fake OXID resolver (COM):** implements `IObjectExporter` — answers `ServerAlive2` with `RPC_S_OK`, and forges **`ResolveOxid2`** to return a `DUALSTRINGARRAY` with TowerID **`ncacn_np`** and endpoint string **`localhost/pipe/roguepotato[\pipe\epmapper]`** plus security bindings naming `NT AUTHORITY\NETWORK SERVICE`. (First attempt with `ncacn_ip_tcp:localhost[9998]` + `IRemUnknown2` + `RpcImpersonateClient()` yielded only an **Identification** token — dead end; the named-pipe transport was the fix.)
4. **Path-validation bug (borrowed from PrintSpoofer, credit @itm4n & @jonasLyk):** `/` in the hostname is normalized into the pipe path → rpcss connects to attacker pipe **`\\.\pipe\roguepotato\pipe\epmapper`** (protocol forces the `\pipe\epmapper` suffix; the `/pipe/roguepotato` prefix redirects it).
5. **Impersonation (not COM):** `ImpersonateNamedPipeClient()` → **NETWORK SERVICE impersonation token** with the rpcss logon-session LUID.
6. **Token kidnapping:** because rpcss/RpcEptMapper (NETWORK SERVICE) *shares a logon session with SYSTEM-token-holding threads* (per Forshaw's "Sharing a Logon Session a Little Too Much", tiraniddo.dev/2020/04/sharing-logon-session-little-too-much.html), enumerate handles in the rpcss process, duplicate a SYSTEM token, `CreateProcessAsUser()`/`CreateProcessWithToken()` (with Session-0 window-station permission fix) → SYSTEM.
- **Requirements:** SeImpersonatePrivilege; attacker-controlled machine reachable on TCP 135 from victim (or local resolver + port proxy); **Works:** Win10 **1809+** / Server **2019+** (exactly where Juicy died).

### 5.5 PrintSpoofer (itm4n, Mar 2020)

- **Coercion (pure RPC, not COM):** MS-RPRN **`RpcRemoteFindFirstPrinterChangeNotificationEx`** (SpoolSample-style trigger; original coercion PoC: github.com/leechristensen/SpoolSample) forces the **Print Spooler (SYSTEM)** to connect to a notification pipe.
- **The bug:** the printer-name validation accepts `\\HOSTNAME/pipe/foo`, which is later normalized to **`\\HOSTNAME\pipe\foo\pipe\spoolss`** — redirecting the SYSTEM spooler into the attacker's pipe server `\\.\pipe\foo\pipe\spoolss` despite `\pipe\spoolss` being occupied.
- **Token half (not COM):** `CreateNamedPipe` → client connects → `ImpersonateNamedPipeClient()` → `OpenThreadToken` → `DuplicateTokenEx` (primary) → `CreateProcessWithTokenW` → SYSTEM.
- **Requirements:** SeImpersonatePrivilege; spooler running. **Works:** Win10, Server 2016/2019 (and older with spooler); **Patch status:** unpatched at publication; treat as *build-dependent today* — later Windows builds hardened spooler behaviors and PrintNotify-based alternatives exist for hardened/no-spooler hosts (flag for verification per target build; medium confidence).

### 5.6 SweetPotato (@_EthicalChaos_ / CCob, 2020)

- **C# unified toolkit**: "Local Service to SYSTEM privilege escalation from Windows 7 to Windows 10 / Server 2019" — bundles the RottenPotato code base, the weaponized JuicyPotato BITS+WinRM approach (credit @decoder_it/@Guitro), RogueWinRM, and PrintSpoofer; auto-attempts the correct technique (`-c` CLSID default BITS `{4991D34B-…}`, `-m` method). COM usage = inherited from Rotten/Juicy (DCOM trigger) + named-pipe half from PrintSpoofer.
- Refs: github.com/CCob/SweetPotato; ethicalchaos.dev/2020/04/13/sweetpotato-local-service-to-system-privesc/; pentestpartners.com/security-blog/sweetpotato-service-to-system/; decoder's companion analysis "We thought they were potatoes but they were beans" (decoder.cloud/2019/12/06/).

### 5.7 JuicyPotatoNG (@decoder_it & @splinter_code, Sep 2022)

- Blog: decoder.cloud/2022/09/21/giving-juicypotato-a-second-chance-juicypotatong/; repo github.com/antonioCoco/JuicyPotatoNG. Default CLSID **`{854A20FB-2D44-457D-992F-EF13785D2B51}` (PrintNotify — "Printer Extensions and Notifications" service, SYSTEM; present Win10/11 + Server 2016/2019/2022)**, default COM-server port **10247**.
- **Problem solved:** post-1809, surviving CLSIDs require the caller to be **INTERACTIVE**, which service accounts are not.
- **Trick 1 (token enrichment):** `LogonUser()` with **logon type 9 (`NewCredentials`)** — LSASS clones the caller's token and **adds the INTERACTIVE SID** (no impersonation privilege needed for the clone) → the enriched token can activate the INTERACTIVE-gated CLSIDs.
- **Trick 2 (token capture):** local COM server on 10247 + **SSPI hook on `AcceptSecurityContext()`** to intercept the privileged DCOM client's NTLM exchange as it is processed in-process — avoids `RpcServerUseProtseqEp` (which would hold the port) and, per the authors, relaxes requirements vs `RpcImpersonateClient()`. Conceptually rooted in Forshaw's Kerberos-DCOM relay work ("Windows Exploitation Tricks: Relaying DCOM Authentication", Oct 2021).
- **Works:** Win10 **1809+ / Win11**, Server **2019+ / 2022** (i.e., post-Juicy world). Verified PoC output: `[+] authresult success {854A20FB-…};NT AUTHORITY\SYSTEM;Impersonation`.
- Also see decoder's companion piece "The impersonation game" (decoder.cloud/2020/05/30/, a.k.a. "Juicy2" lineage).

### 5.8 LocalPotato (@decoder_it & @splinter_code, Feb 2023) — CVE-2023-21746

**Precision — what is COM vs not:** *COM/DCOM is used only as the trigger; the vulnerability is in NTLM local authentication inside LSASS (`msv1_0.dll`); the write primitive is SMB. It is NOT a DCOM bug.*

1. **Trigger (COM):** `CoGetInstanceFromIStorage` with a SYSTEM-authenticating CLSID — default **`{854A20FB-2D44-457D-992F-EF13785D2B51}` (PrintNotify)**; alternatives: **`{90F18417-F0F1-484E-9D3C-59DCEEE5DBD8}` (ActiveX Installer Service "AxInstSv", Win10/11 only)** and **`{A9819296-E5B3-4E67-8226-5E72CE9E1FB7}` (Universal Print Management Service "McpManagementService", Win11 + Server 2022)**. The fake local OXID resolver (JuicyPotatoNG-style SSPI hooks, port 10271 default) returns security bindings containing a **crafted SPN `cifs/127.0.0.1`**, causing the privileged DCOM client to initiate NTLM with `ISC_REQ_UNVERIFIED_TARGET_NAME` and that SPN.
2. **The bug (NTLM/LSASS, not COM):** in *NTLM local authentication*, Type 2 messages carry a security-context handle referencing an in-memory server context. The attacker runs a rogue client against the **local SMB server (127.0.0.1:445)** in parallel, and **swaps the context handles** between the two flows — LSASS binds the privileged (SYSTEM) identity to the *attacker's* SMB session ("Context Swapping").
3. **Result:** attacker's SMB client is authenticated to `\\127.0.0.1\C$` **as SYSTEM** → arbitrary file read/write (PoC writes raw SMB2 packets per MS-SMB2).
4. **Weaponization:** write `SprintCSP.dll` to System32 + trigger `SvcRebootToFlashingMode` (StorSvc RPC, BlackArrowSec's RpcClient — released 2023-02-13), or overwrite `printconfig.dll` and instantiate **PrintNotify** (the authors' Server 2022 demo), or XPS Print Job / NetMan DLL hijack.
5. **Patch:** Jan 2023 (CVE-2023-21746): `SsprHandleChallengeMessage` — if `ISC_REQ_UNVERIFIED_TARGET_NAME` set and SPN present → SPN nulled → SMB anti-reflection denies `cifs/127.0.0.1`. **HTTP/WebDAV scenario remained unpatched (Microsoft decision)**: "LocalPotato HTTP edition" (decoder.cloud/2023/11/03/localpotato-http-edition/). 0patch covers EOL versions.

### 5.9 The rest of the family — verified existence & classification

| Tool | Author/Year | Mechanism | COM in chain? | Works on |
|---|---|---|---|---|
| **GodPotato** | BeichenDream, Dec 2022 | Novel DCOM trigger; author: "some defects in rpcss when dealing with OXID"; needs only SeImpersonatePrivilege; .NET 2/3.5/4 builds | **Yes (trigger)** — service-DCOM-object activation + impersonation of the DCOM callback | Win8–11, Server 2012–2022 |
| **DCOMPotato** | zcgonvh | "Some Service DCOM Object and SeImpersonatePrivilege abuse" — variants `PrinterNotifyPotato.exe`, `McpManagementPotato.exe`; attacker-supplied `IUnknown` callback → `CoImpersonateClient()` in its `QueryInterface` impersonates the SYSTEM caller (these objects default to `RPC_C_IMP_LEVEL_IMPERSONATE`) | **Yes (trigger + callback impersonation)** | Win10/11, Server 2012–2022 |
| **PrintNotifyPotato** | BeichenDream, late 2022 | PrintNotify CLSID + fake `IUnknown`/pointer-moniker callback; works even with legacy Spooler disabled | **Yes** | Win10/11, Server 2012–2022 |
| **EfsPotato** | zcgonvh, 2021 | MS-EFSR (`EfsRpcOpenFileRaw`/`EfsRpcEncryptFileSrv` PetitPotam-style) coercion of SYSTEM to attacker pipe `\\.\pipe\lsarpc` (alt: efsrpc/samr/lsass/netlogon) + named-pipe impersonation; CVE-2021-36942 patch bypass notes by @xassiz | **No — pure RPC coercion + pipe impersonation** | Servers 2012–2022 era (pre-EFSR hardening); C# |
| **SharpEfsPotato** | bugch3ck | EfsRpc + pipe impersonation built from SweetPotato + SharpEfsTrigger | No | similar |
| **CoercedPotato** | @Hack0ura & @Prepouce (Hackvens), 2023 | Automated multi-coercion: tries MS-RPRN (`RpcRemoteFindFirstPrinterChangeNotification[Ex]`) then MS-EFSR functions (14 variants) against local pipe servers `\\.\pipe\coerced\pipe\spoolss` / `…\srvsvc` + impersonation; French writeup blog.hackvens.fr/articles/CoercedPotato.html | **No — pure RPC coercion** | Win10/11, Server 2022 (verified screenshot build 22621) |
| **PetitPotato** | wh0amitz | Local PetitPotam (EFSRPC) → pipe | No | — |
| **RasmanPotato** | crisprss | Coerces **RasMan** service (SYSTEM) via its RPC interface → pipe impersonation | No | Win10, Server 2012–2019 |
| **BadPotato** | BeichenDream | itm4n's PrintSpoofer ported to C# | No | Win8–10, 2012–2019 |
| **CandyPotato** | klezVirus | Pure C++ weaponized RottenPotatoNG | Yes (Rotten lineage) | Win10/2016 era |
| **MultiPotato** | S3cur3Th1sSh1t | Multi-trigger combo | mixed | — |
| **SigmaPotato** | tylerdotrar, 2023–24 | Modernized GodPotato-fork style; `--revshell`, in-memory `[SigmaPotato]::Main()` via .NET reflection | Yes (DCOM lineage) | Win8–11, 2012–2022 |
| **GenericPotato** | Micah van Deusen, 2021 | Generic SeImpersonation → SYSTEM via modified SSPI/HTTP named-pipe-less approach (micahvandeusen.com/the-power-of-seimpersonation/) | partial | — |
| **GhostPotato** | Shenanigans Labs, Nov 2019 | MS08-068-style SMB relay bypass via NTLM challenge-cache (300 s) with modified impacket | No | unpatched-at-the-time SMB relay |
| **RemotePotato0** | antonioCoco/SentinelOne, 2021 | Cross-protocol relay RPC→HTTP, "Won't Fix" — **remote** user→DA, not local LPE; boundary case | partial | — |
| **CertPotato** | SensePost, 2022 | ADCS cert path from virtual/network service accounts | No | — |
| **LonelyPotato** | decoder, Dec 2017 | RottenPotato standalone (no Meterpreter) | Yes | ≤1803 |
| **"SharpPotato"** | — | **No well-known tool by this exact name exists** (likely confusion with SweetPotato/BadPotato/SharpEfsPotato). State this to preempt questions. | — | — |

### 5.10 Potato decision tree (text form)

1. `whoami /priv` → SeImpersonatePrivilege or SeAssignPrimaryTokenPrivilege present? (No → not a potato target.)
2. OS ≤ Win10 1803 / Server 2016 → **RottenPotatoNG / JuicyPotato** (BITS or list CLSID).
3. OS ≥ Win10 1809 / Server 2019 → can victim reach your box on TCP 135 (or local port-proxy)? → **RoguePotato**. Else → interactive-capable or service account? → **JuicyPotatoNG** (LogonUser type-9 trick) / **GodPotato / DCOMPotato / PrintNotifyPotato** (service DCOM callbacks).
4. Spooler up, Win10/2016-2019 → **PrintSpoofer** (C#: BadPotato). Spooler down/hardened → **EfsPotato / CoercedPotato** coercion alternatives.
5. Need file-write primitive on ≤ Jan-2023 builds → **LocalPotato** (+ StorSvc/PrintNotify weaponization); patched → LocalPotato HTTP/WebDAV edition.
6. .NET 4 present → SweetPotato / GodPotato-NET4 / SigmaPotato for opsec-friendly in-memory execution.

---

## 6. TRAPPED COM OBJECTS (Forshaw, Project Zero, Jan 30 2025)

**Source:** "Windows Bug Class: Accessing Trapped COM Objects with IDispatch" — projectzero.google/2025/01/windows-bug-class-accessing-trapped-com.html (mirror googleprojectzero.blogspot.com). (Post is dated 2025-Jan-30.)

### 6.1 The bug class

Object-oriented remoting (DCOM, .NET Remoting) marshals returned objects **by reference** by default — the object stays ("is trapped") in the server process while the client gets a remote pointer. If the object is not safe to expose across the boundary, the client now drives privileged code in the server. Three introduction scenarios:

1. **Unsafe object shared inadvertently** — **CVE-2019-0555**: WinRT needed an XML document; devs bolted runtime interfaces onto the XML DOM Document v6 COM object assuming no XSLT → malicious client `QueryInterface`s the legacy `IXMLDOMDocument` → XSLT script execution → sandbox escape.
2. **Asynchronous marshaling primitive** — .NET classes both serializable and `MarshalByRefObject` (e.g., `FileInfo`/`DirectoryInfo`): send serialized copy to server, read it back → marshaled **by reference** → trapped live object → create files as server (implemented in Forshaw's **ExploitRemotingService** tool).
3. **Abusing the platform's object-instantiation plumbing** — coerce a path to `CoCreateInstance` with attacker CLSID and get the object back: **CVE-2017-0211** (Structured Storage exposed across boundary; `IPropertyBag` → create arbitrary COM object in server → XML DOM → XSLT EoP).

### 6.2 The IDispatch / type-library vector (the new part)

- Any OOP server exposing **`IDispatch`** must vend type info: client calls `IDispatch::GetTypeInfo` → **`ITypeInfo`**, itself marshaled by reference → **trapped in server**.
- `ITypeInfo::Invoke` is *not* remotable, but **`ITypeInfo::CreateInstance` IS** — and it calls `CoCreateInstance` **in the server process**. The type info must describe a **CoClass** (carries a CLSID); reach it via `ITypeInfo::GetContainingTypeLib` → `ITypeLib` → enumerate classes → including **referenced type libraries** (`stdole` is virtually always referenced).
- Worked example (verbatim methodology from the post, OleViewDotNet PowerShell module):
  - `Get-ComClass -Service` → enumerate interfaces → filter `IsDispatch` → 5 candidates on a default box: **WaaSRemediation `{72566E27-1ABB-4EB3-B4F0-EB431CB1CB32}`**, SearchGatheringManager `{9E175B68-F52A-11D8-B9A5-505054503030}`, SearchGathererNotification `{9E175B6D-…}`, AutomaticUpdates `{BFE18E9C-6D87-4450-B37C-E02F0B373803}`, Microsoft.SyncShare.SyncShareFactoryClass `{DA1C0281-456B-4F14-A46D-8ED2E21A866F}`.
  - `New-ComObject -Clsid 72566e27-…` → `Import-ComTypeLib` → verify OOP with `Get-ComObjRef` (shows svchost.exe) → `$lib.Parse()` → `ReferencedTypeLibs` → **stdole 2.0 `{00020430-0000-0000-C000-000000000046}`** → classes **StdFont `{0BE35203-8F91-11CE-9DE3-00AA004BB851}`** and StdPicture `{0BE35204-…}` — both registered **InProcServer32 only (`oleaut32.dll`)** → instantiating one via the *remote* type library plants it inside the service: navigate `GetRefTypeOfImplType(0)` → `GetRefTypeInfo` → `GetContainingTypeLib` → `GetTypeInfoOfGuid(<StdFont CLSID>)` → `CreateInstance()` → `Get-ComObjRef` proves object lives in the service process. (StdPicture has an OOP check; StdFont doesn't. Forshaw found no direct EoP in the font object.)
- **Type-library/proxy substitution → type confusion (why substitution works):** the stub for the remoted interface is *generated from the type library*; control the TLB (registration swap or referencing an OOP TLB) and you control the server-side stub's type view — classic type confusion (his earlier NGEN-COM attack overwrote the KnownDlls handle this way). Microsoft's countermeasure now in `oleaut32.dll`: **`VerifyTrust`** — when loading a TLB inside a protected process, requires cached signing level **≥ 12 (Windows)** via `NtGetCachedSigningLevel`/`NtSetCachedSigningLevel`; raw (non-PE) TLBs fail with `STATUS_INVALID_IMAGE_FORMAT`. This same hardening blocks ROT-based OOP type-library references.

### 6.3 PPL injection chain (the flagship PoC)

`WaaSRemediationAgent` runs in **`WaaSMedicSvc` as PPL `PsProtectedSignerWindows-Light`** (`$cls.AppIDEntry.ServiceProtectionLevel = WindowsLight`) — the service that also gates access to LSASS-level protected processes. Chain:

1. Set `HKLM\SOFTWARE\Microsoft\.NETFramework\AllowDCOMReflection = 1` (DWORD) — re-enables .NET DCOM reflection disabled since **MS14-009**; also `OnlyUseLatestCLR = 1` to force CLR v4 (else v2 is loaded). *(Admin needed for HKLM; but per-user HKCU COM/.NET registration can substitute — Forshaw: "the general technique does not [require admin]" if you find any suitable IDispatch-exposing server.)*
2. **`TreatAs` hijack**: point `HKLM\SOFTWARE\Classes\CLSID\{0BE35203-8F91-11CE-9DE3-00AA004BB851}\TreatAs` (StdFont) at the .NET **`System.Object`** COM class.
3. Create `WaaSRemediationAgent` OOP → walk IDispatch→stdole→StdFont `CreateInstance` → **CLR loads inside the PPL** and returns `System.Object`.
4. `GetType` → .NET reflection → **`System.Reflection.Assembly::Load(byte[])`** → unsigned code executes in PPL-Windows → instantiate an object in the loaded assembly → run code (LSASS-with-PPL, protected AV/EDR processes now reachable). Cleanup registry.
5. **Windows 11 24H2 wrinkle:** 64-bit `mscorlib.tlb` is a raw TLB → fails `VerifyTrust` signing-level (STATUS_INVALID_IMAGE_FORMAT); the **32-bit** `Framework\v4.0.30319\mscorlib.tlb` is PE-packed (MZ) → set cached signing level (or swap TLB registration path to the 32-bit file) → PoC works again. Forshaw did **not** release his PoC (C++; "very similar to my CVE-2014-0257 exploit" — github.com/tyranid/IE11SandboxEscapes).
- **Public third-party PoCs:** Mohamed Fakroud's writeup + **github.com/T3nb3w/ComDotNetExploit** (PPL injection); IBM X-Force Red "Fileless lateral movement with trapped COM objects" + **github.com/xforcered/ForsHops** (`forshops.exe [target] [c:\path\to\assembly]`; detection: TreatAs key on StdFont CLSID, `AllowDCOMReflection`/`OnlyUseLatestCLR` values, CLR load inside WaaSMedicSvc svchost — Samir Bousseaden's guidance; YARA strings include WaaSRemediation CLSID `{72566E27-…}` and `{34050212-8AEB-416D-AB76-1E45521DB615}`); **Outflank (Kyle Avery, Jul 2025)** used LLM-assisted enumeration to find **alternative IDispatch classes** that avoid the PPL CLR-load limitation entirely (outflank.nl/blog/2025/07/29/accelerating-offensive-research-with-llm/).
- Framing: LPE angle = sandbox/AppContainer escapes and PPL injection (Microsoft does not treat PPL as a security boundary → fixes ship late, in new Windows versions).

---

## 7. OTHER DOCUMENTED COM LPE VECTORS (verified)

1. **UPnP Device Host + Update Orchestrator chain (NCC Group / Fox-IT, Nov 2019):**
   - **CVE-2019-1405** — logical flaw in UPnP Device Host service COM objects `UPnPContainerManager`/`UPnPContainerManager64` implementing undocumented `IUPnPContainerManager` (IID `6D8FF8D4-730D-11D4-BF42-00B0D0118B56`; methods `ReferenceContainer`, `UnReferenceContainer`, `CreateInstance`, `CreateInstanceWithProgID`, `Shutdown`) → any local user executes commands as **LOCAL SERVICE**. Fixed Nov 2019 by adding Administrators-only access checks to the dangerous methods.
   - **CVE-2019-1322** — service misconfiguration: members of the **`SERVICE` group could reconfigure UsoSvc** (`sc stop UsoSvc` → `sc config UsoSvc binpath= …` → `sc start`) running as SYSTEM. Fixed Oct 2019 by stripping Full Control from the SERVICE group.
   - Chained: any local user → SYSTEM on Win10 1803–1903. PoC: **COMahawk** (`COMahawk64.exe`). Ref: fox-it.com writeup ("CVE-2019-1405 and CVE-2019-1322 – Elevation to SYSTEM via the UPnP Device Host Service and the Update Orchestrator Service", Langlois & Torkington). *The canonical "service exposes dangerous COM API to everyone" teaching example — and the methodology section of that post is a ready-made OleViewDotNet walkthrough.*
2. **COM Aggregate Marshaler & type-library bugs (Forshaw, May 2017):** **CVE-2017-0213** (Aggregate Marshaler `Finish_RemQIAndUnmarshal2`; PoC uses BITS `SetNotifyInterface` → type library load under impersonation → **scriptlet moniker** → code exec in BITS as SYSTEM; exploit-db 42020; later CISA KEV) and **CVE-2017-0214** (type-library input validation).
3. **Session Moniker / interactive-user DCOM (2017):** CVE-2017-0100 + bad-fix CVE-2017-0298. Bug pattern: `RunAs=Interactive User` + cross-session activation + missing client verification.
4. **WalletService memory corruption over COM — CVE-2020-1362** (Q4n/Haoran Qin + Zhiniang Peng, Jul 2020): WalletService COM server (`Wallet::WalletCustomProperty::SetGroup` OOB write → `SetLabel` BSTR pointer write-what-where → fake vtable → redirect virtual call to `dxgi!ATL::CComObject<CDXGIAdapter>::vector deleting destructor` whose `LoadLibraryExW` uses a writable global path → load attacker DLL as SYSTEM; chained with CVE-2020-1361 heap leak). Medium IL → SYSTEM, verified on 18363. Writeup: github.com/Q4n/CVE-2020-1362. **Pattern: DCOM method parameters → memory corruption in privileged server.**
5. **Per-user COM hijack into privileged processes (HKCU\Software\Classes\CLSID\{…}\InprocServer32 / TreatAs / AppID paths):** user-writable per-user COM registrations shadow machine registrations for classes activated *in the user's logon session*. Classic uses are UAC bypass & persistence (bohops "Abusing the COM Registry Structure" parts 1–2; MITRE ATT&CK **T1122 Component Object Model Hijacking**), but it becomes **LPE/PPL-relevant** when a higher-privileged in-session process (or an IDispatch trapped-object server) activates the class — Forshaw explicitly notes the per-user variant of the StdFont TreatAs hijack for PPL injection *without* admin. Guidance: present as "check what identity activates user-scope classes."
6. **.NET COM interop into privileged processes:** any registered .NET COM class loads the CLR (mscoree) into the activating process — including SYSTEM services and PPL. Leveraged by CVE-2014-0257 (Forshaw IE sandbox escape, public PoC), the trapped-object chain, and historically by .NET DCOM reflection (`System.Type` over DCOM → arbitrary reflection; fixed by **MS14-009**, re-enablable via `AllowDCOMReflection`; `OnlyUseLatestCLR` selects CLR v4). Pattern: **registry values + TreatAs + .NET class = unsigned code in signed processes.**
7. **WinRT broker / runtime-class bugs:** WinRT is COM underneath (runtime classes, activation via combase, broker processes crossing IL/AppContainer boundaries). Verified example in-scope: **CVE-2019-0555** (XML DOM through WinRT). Guidance: brokers = OOP COM servers with sandbox-facing activation permissions — same triage.
8. **DiagHub sibling CVEs:** CVE-2018-0952 (Atredis PoC) and the later DiagHub case-sensitivity directory-traversal EoP (irsl PoC) — reinforce "DCOM service with file-manipulation methods = recurring CVE farm."
9. **Adjacent (flag as *not* COM, avoid mislabeling):** Perfusion (itm4n) is the **RpcEptMapper registry-key ACL** bug (Win7/2008R2/8/2012) — RPC, not COM; FullPowers recovers LOCAL/NETWORK SERVICE default privileges (task scheduler, not COM); CVE-2020-1337 Print Spooler file-write — RPC.

---

## 8. RESEARCH METHODOLOGY — FINDING NEW COM LPE BUGS

### 8.1 Enumeration

- **OleViewDotNet** (github.com/tyranid/oleviewdotnet; Forshaw, .NET 4; PS module: `Install-Module OleViewDotNet` — **PowerShell 5 only**, not 7+):
  - GUI: `Registry → Local Services` (COM objects hosted in services, with filter), `Registry → CLSIDs by Server`, object node → **View Launch Permissions / View Access Permissions** (decoded AppID SDDLs).
  - PowerShell: `Get-ComClass -Service`, `Get-ComInterface -Class $_`, `New-ComObject -Clsid …`, `Import-ComTypeLib -Object …`, `Get-ComObjRef` (proves OOP + host PID/name), `Get-ComClass -Clsid …` → `.Servers` (InProc vs Local), `.AppIDEntry.ServiceProtectionLevel` (PPL check), `.AppIDEntry` (RunAs, service name).
- Raw registry: `HKLM\SOFTWARE\Classes\CLSID`, `…\AppID\{guid}` values `LaunchPermission`, `AccessPermission`, `RunAs`, `Service`, `ServiceProtectionLevel`; machine defaults under `HKLM\SOFTWARE\Microsoft\Ole`. Per-user shadow: `HKCU\Software\Classes\CLSID`.
- Existing abusable-CLSID corpora: ohpe.it/juicy-potato/CLSID/ (per-OS), strontic xcyclopedia CLSID library, juicy-potato repo README tables.
- RPC/RPCSS surface: RpcView (endpoint/interface dump), impacket `rpcdump.py`, Wireshark DCERPC + DCOM (IObjectExporter/IRemUnknown2) dissectors.

### 8.2 Target triage checklist

1. OOP server (`LocalServer32` or service-hosted AppID). 2. Runs as SYSTEM (or LOCAL/NETWORK SERVICE for hops). 3. Launch+access ACLs include your principal (Authenticated Users / INTERACTIVE / AppContainer-LPAC for sandbox escapes). 4. Methods that touch: filesystem paths, registry, process creation, `LoadLibrary*`, `SetNamedSecurityInfo`, token APIs, or that accept interface pointers (callback impersonation, DCOMPotato pattern) or return objects (trapped-object pattern). 5. Bonus: `IDispatch` present (type-library vector), .NET interop, `RunAs=Interactive User`.

### 8.3 Static RE checklist (Forshaw's DiagHub recipe, generalized)

1. OleViewDotNet: create instance → Marshal ⇒ View Properties → capture **PID + IPID + OXID + string bindings + authn services**.
2. OVDN process view (needs admin + configured symbols: dbghelp from WinDbg + `srv*https://msdl.microsoft.com/download/symbols`) → IPID entry → **interface pointer + server-side VTable address** (e.g., `DiagnosticsHub_StandardCollector_Runtime+0x36C78`).
3. WinDbg attach: `dqs <module>+<offset> L<nMethods>` → symbolized method names (public symbols on MS components).
4. OVDN **View Proxy Definition** → decompiles NDR stubless bytecode → method signatures; **Structures tab** → marshaled struct layouts → rebuild the interface in C++/C#.
5. In IDA/Ghidra: xref command strings (itm4n's `"StartScan"` technique), map `_guard_dispatch_icall_fptr` indirect calls to VTable slots via dynamic breakpoints; match client proxy slots (`ObjectStublessClientN`) to server symbols.
6. Hunt the failure modes: missing/late `CoImpersonateClient`/`RpcImpersonateClient`, early `RevertToSelf`, identify-level acceptance, device-map/symlink handling under impersonation, `RunAs` mismatches, type-library/TLB trust, `TreatAs`, unmarshaled-object safety.

### 8.4 Dynamic harness

Minimal C++ client: `CoInitializeEx` → `CoCreateInstance(CLSCTX_LOCAL_SERVER)` → `CoSetProxyBlanket(…, RPC_C_IMP_LEVEL_IMPERSONATE, …)` → invoke methods with controlled inputs; log server-side behavior with **Process Monitor** (filter: server PID; file/reg/LoadImage ops as SYSTEM), API Monitor, WinDbg breakpoints on `CoImpersonateClient`, `RpcImpersonateClient`, `CreateFileW`, `RegCreateKeyExW`, `LoadLibraryExW`, `SetNamedSecurityInfo`; ETW providers Microsoft-Windows-RPC / COM for call tracing; TokenViewer (Forshaw) to inspect captured tokens (type: impersonation vs identification — the #1 source of "exploit didn't work" confusion).

### 8.5 Fuzzing marshal paths

- Mutate marshaled NDR buffers against the **NDR interpreter stubs** (the exact layer OVDN decompiles); watch for OOB in server-side unmarshalling (WalletService class) and for marshal-by-reference object leaks.
- Academic/industrial: **COMRace** (USENIX Security '22 — data-race detection in COM objects); **Black Hat EU 2024 "Enhancing Automatic Vulnerability Discovery for Windows RPC/COM in New Ways"** (harness-generation talk); RPC-aware fuzzing driven from IDA-generated clients; interface brute-calling via OVDN PowerShell (`New-ComObject` + method invocation) as a poor-man's fuzzer.

### 8.6 Patch diffing

BinDiff/Ghidra `rpcss.dll`, `combase.dll`, `ole32.dll`, `oleaut32.dll`, `usosvc.dll`, `usocoreworker`, DiagHub runtime. What to look for (proven patterns): new ACL checks in methods (CVE-2019-1405 fix), service-SD changes (CVE-2019-1322 fix), **silent** binding-builder changes (1809 `MakeBinding`→`CreateRemoteBindingToOR`), image-load policy (`ProcessImageLoadPolicy`, 19H1), signing-level enforcement (`VerifyTrust` in oleaut32), NTLM package changes (`SsprHandleChallengeMessage`, CVE-2023-21746, diff `msv1_0.dll`).

---

## 9. DIAGRAM SPECS (covered by DIAGRAM_SPEC d12–d17)

1. Impersonation Contract sequence diagram (d06/d12).
2. DiagHub chain (d13): StorSvc `SvcMoveFileInheritSecurity` + hard link → `WRITE_DAC` on `license.rtf` → overwrite → `CoCreateInstance({42CBFAA7-…})` → CreateSession → AddAgent → LoadLibraryExW as SYSTEM; mitigation ribbon: 19H1 ProcessImageLoadPolicy.
3. RoguePotato chain (d15): swimlane with COM lanes (1–4) vs pipe/token (5–7).
4. Potato decision tree (d16 timeline + text tree): color-code COM trigger vs RPC coercion vs NTLM/LSASS; build ranges per branch.
5. Trapped COM object flow (d17): IDispatch→ITypeInfo→stdole→StdFont CreateInstance→TreatAs→System.Object in PPL→Assembly.Load; VerifyTrust side panel.
6. DCOM OBJREF anatomy (d04): the exact bytes every potato forges.

---

## 10. REFERENCES

**Project Zero / Forshaw (primary):**
- Windows Exploitation Tricks: Exploiting Arbitrary File Writes for Local Elevation of Privilege (DiagHub) — https://googleprojectzero.blogspot.com/2018/04/windows-exploitation-tricks-exploiting.html (mirror: https://projectzero.google/2018/04/windows-exploitation-tricks-exploiting.html)
- P0 issue 1428 (StorSvc `SvcMoveFileInheritSecurity`, full exploit incl. DiagHub loader) — https://bugs.chromium.org/p/project-zero/issues/detail?id=1428
- P0 issue 325 (DCOM DCE/RPC Local NTLM Reflection → CVE-2015-2370) — https://bugs.chromium.org/p/project-zero/issues/detail?id=325
- Windows Bug Class: Accessing Trapped COM Objects with IDispatch — https://projectzero.google/2025/01/windows-bug-class-accessing-trapped-com.html (mirror: https://googleprojectzero.blogspot.com/2025/01/windows-bug-class-accessing-trapped-com.html)
- Windows Exploitation Tricks: Relaying DCOM Authentication (basis of JuicyPotatoNG trick) — https://googleprojectzero.blogspot.com/2021/10/windows-exploitation-tricks-relaying.html
- Sharing a Logon Session a Little Too Much (NETWORK SERVICE→SYSTEM; RoguePotato dependency) — https://www.tiraniddo.dev/2020/04/sharing-logon-session-little-too-much.html
- CVE-2014-0257 PoC (.NET DCOM reflection, referenced for trapped-object PoC) — https://github.com/tyranid/IE11SandboxEscapes
- OleViewDotNet — https://github.com/tyranid/oleviewdotnet

**itm4n:**
- Weaponizing Privileged File Writes with the USO Service — Part 1 — https://itm4n.github.io/usodllloader-part1/
- Weaponizing Privileged File Writes with the USO Service — Part 2 — https://itm4n.github.io/usodllloader-part2/
- UsoDllLoader — https://github.com/itm4n/UsoDllLoader
- PrintSpoofer — Abusing Impersonation Privileges on Windows 10 and Server 2019 — https://itm4n.github.io/printspoofer-abusing-impersonate-privileges/
- PrintSpoofer repo — https://github.com/itm4n/PrintSpoofer

**decoder.cloud / splinter_code:**
- No more JuicyPotato? Old story, welcome RoguePotato! — https://decoder.cloud/2020/05/11/no-more-juicypotato-old-story-welcome-roguepotato/
- No more rotten/juicy potato? (1809 silent fix) — https://decoder.cloud/2018/10/29/no-more-rotten-juicy-potato/
- Giving JuicyPotato a second chance: JuicyPotatoNG — https://decoder.cloud/2022/09/21/giving-juicypotato-a-second-chance-juicypotatong/
- The impersonation game (Juicy2) — https://decoder.cloud/2020/05/30/the-impersonation-game/
- The lonely potato — https://decoder.cloud/2017/12/23/the-lonely-potato/
- From NETWORK SERVICE to SYSTEM — https://decoder.cloud/2020/04/01/from-network-service-to-system/
- We thought they were potatoes but they were beans (SweetPotato analysis) — https://decoder.cloud/2019/12/06/we-thought-they-were-potatoes-but-they-were-beans/
- LocalPotato — When Swapping The Context Leads You To SYSTEM — https://decoder.cloud/2023/02/13/localpotato-when-swapping-the-context-leads-you-to-system/
- LocalPotato HTTP edition — https://decoder.cloud/2023/11/03/localpotato-http-edition/
- LocalPotato repo — https://github.com/decoder-it/LocalPotato ; technical writeup — https://www.localpotato.com/localpotato_html/LocalPotato.html
- RoguePotato repo — https://github.com/antonioCoco/RoguePotato ; JuicyPotatoNG repo — https://github.com/antonioCoco/JuicyPotatoNG ; RogueWinRM — https://github.com/antonioCoco/RogueWinRM
- Talk slides: "10 Years of Windows Privilege Escalations with Potatoes" (Antonio Cocomazzi, POC2023/Troopers24) — https://powerofcommunity.net/poc2023/AntonioCocomazzi.pdf and https://troopers.de/downloads/troopers24/TR24_10_years_of_Windows_Privilege_Escalation_with_Potatoes_CYZBJ3.pdf

**FoxGlove / ohpe:**
- Rotten Potato — Privilege Escalation from Service Accounts to SYSTEM — https://foxglovesecurity.com/2016/09/26/rotten-potato-privilege-escalation-from-service-accounts-to-system/
- Hot Potato — https://foxglovesecurity.com/2016/01/16/hot-potato/
- Abusing Token Privileges for Windows LPE — https://foxglovesecurity.com/2017/08/25/abusing-token-privileges-for-windows-local-privilege-escalation/
- RottenPotato repo — https://github.com/foxglovesec/RottenPotato ; RottenPotatoNG — https://github.com/foxglovesec/RottenPotatoNG
- JuicyPotato (abusing the golden privileges) — http://ohpe.it/juicy-potato/ ; CLSID lists — http://ohpe.it/juicy-potato/CLSID/ ; repo — https://github.com/ohpe/juicy-potato

**CVE-2015-2370 ecosystem / CVE-2017-0100:**
- Exploiting MS15-076 (CVE-2015-2370) — NetSPI (Trebuchet) — https://www.netspi.com/blog/technical-blog/web-application-pentesting/exploiting-ms15-076-cve-2015-2370/
- Microsoft Patch Tuesday July 2015 (MS15-076 context) — https://blog.talosintelligence.com/microsoft-patch-tuesday-july-2015/
- CVE-2017-0100 advisory text (HelpPane) — https://msrc.microsoft.com/update-guide/vulnerability/CVE-2017-0100 (NVD mirror: https://nvd.nist.gov/vuln/detail/CVE-2017-0100)
- "Windows: Bad Fix for COM Session Moniker EoP" (CVE-2017-0298) — mirrored at https://sploitus.com/exploit?id=1337DAY-ID-28134

**Other potatoes / coercion:**
- SweetPotato — https://github.com/CCob/SweetPotato ; blog — https://ethicalchaos.dev/2020/04/13/sweetpotato-local-service-to-system-privesc/ ; Pentest Partners — https://www.pentestpartners.com/security-blog/sweetpotato-service-to-system/
- SpoolSample (Lee Christensen) — https://github.com/leechristensen/SpoolSample
- EfsPotato — https://github.com/zcgonvh/EfsPotato ; SharpEfsPotato — https://github.com/bugch3ck/SharpEfsPotato ; DCOMPotato — https://github.com/zcgonvh/DCOMPotato
- GodPotato — https://github.com/BeichenDream/GodPotato ; BadPotato — https://github.com/BeichenDream/BadPotato ; PrintNotifyPotato — https://github.com/BeichenDream/PrintNotifyPotato
- CoercedPotato — https://github.com/Prepouce/CoercedPotato ; writeup (FR) — https://blog.hackvens.fr/articles/CoercedPotato.html
- PetitPotato — https://github.com/wh0amitz/PetitPotato ; RasmanPotato — https://github.com/crisprss/RasmanPotato ; CandyPotato — https://github.com/klezVirus/CandyPotato ; MultiPotato — https://github.com/S3cur3Th1sSh1t/MultiPotato ; SigmaPotato — https://github.com/tylerdotrar/SigmaPotato
- GhostPotato (Shenanigans Labs) — https://shenaniganslabs.io/2019/11/12/Ghost-Potato.html
- GenericPotato — https://micahvandeusen.com/the-power-of-seimpersonation/
- RemotePotato0 / Relaying Potatoes (SentinelOne) — https://www.sentinelone.com/labs/relaying-potatoes-another-unexpected-privilege-escalation-vulnerability-in-windows-rpc-protocol/
- CertPotato (SensePost) — https://sensepost.com/blog/2022/certpotato-using-adcs-to-privesc-from-virtual-and-network-service-accounts-to-local-system/
- Awesome-potatoes study notes — https://github.com/bodik/awesome-potatoes ; "In the Potato family, I want them all" — https://hideandsec.sh/books/windows-sNL/page/in-the-potato-family-i-want-them-all ; Potatoes overview — https://jlajara.gitlab.io/Potatoes_Windows_Privesc

**Trapped COM ecosystem:**
- IBM X-Force: Fileless lateral movement with trapped COM objects — https://www.ibm.com/think/news/fileless-lateral-movement-trapped-com-objects ; ForsHops — https://github.com/xforcered/ForsHops
- Abusing IDispatch for Trapped COM Object Access & Injecting into PPL Processes (Mohamed Fakroud) — https://mohamed-fakroud.gitbook.io/red-teamings-dojo/abusing-idispatch-for-trapped-com-object-access-and-injecting-into-ppl-processes ; PoC — https://github.com/T3nb3w/ComDotNetExploit
- Outflank: Accelerating Offensive R&D with LLMs (alternative trapped-object classes) — https://www.outflank.nl/blog/2025/07/29/accelerating-offensive-research-with-llm/
- RemoteMonologue (IBM X-Force, DCOM NTLM coercion) — https://www.ibm.com/think/x-force/remotemonologue-weaponizing-dcom-ntlm-authentication-coercions

**Other COM LPE CVEs:**
- NCC Group Fox-IT: CVE-2019-1405 and CVE-2019-1322 — Elevation to SYSTEM via UPnP Device Host and Update Orchestrator — https://www.fox-it.com/be-en/cve-2019-1405-and-cve-2019-1322-elevation-to-system-via-the-upnp-device-host-service-and-the-update-orchestrator-service/
- CVE-2020-1362 WalletService writeup — https://github.com/Q4n/CVE-2020-1362
- CVE-2017-0213 advisory/exploit — https://www.exploit-db.com/exploits/42020/ ; analysis — https://xz.aliyun.com/t/148/
- CVE-2018-0952 DiagHub PoC — https://github.com/atredispartners/CVE-2018-0952-SystemCollector ; DiagHub case-sensitivity EoP — https://github.com/irsl/microsoft-diaghub-case-sensitivity-eop-cve
- Intro to privileged file operation abuse (OffSec/Almond) — https://offsec.almond.consulting/intro-to-file-operation-abuse-on-Windows.html
- Troopers19: Abusing privileged file operations — https://troopers.de/downloads/troopers19/TROOPERS19_AD_Abusing_privileged_file_operations.pdf
- DCOM Turns 30: Revisiting a Legacy Interface in the Modern Threatscape (hack.lu 2025 slides) — https://d3lb3.github.io/assets/hacklu_2025.pdf
- COMRace (USENIX Security '22) — talk https://www.youtube.com/watch?v=9bBh2YEqVMA ; BHEU 2024 RPC/COM vuln discovery — https://www.youtube.com/watch?v=VQiQuLo0v58
- bohops: Abusing COM Registry Structure pt.1 — https://bohops.com/2018/06/28/abusing-com-registry-structure-clsid-localserver32-inprocserver32/ ; pt.2 — https://bohops.com/2018/08/18/abusing-the-com-registry-structure-part-2-loading-techniques-for-evasion-and-persistence/

**Microsoft docs (impersonation contract):**
- CoSetProxyBlanket — https://learn.microsoft.com/en-us/windows/win32/api/combaseapi/nf-combaseapi-cosetproxyblanket
- CoQueryClientBlanket — https://learn.microsoft.com/en-us/windows/win32/api/combaseapi/nf-combaseapi-coqueryclientblanket
- CoImpersonateClient — https://learn.microsoft.com/en-us/windows/win32/api/combaseapi/nf-combaseapi-coimpersonateclient
- Cloaking — https://learn.microsoft.com/en-us/windows/win32/com/cloaking
- RPC_C_IMP_LEVEL enumeration — https://learn.microsoft.com/en-us/windows/win32/api/wtypesbase/ne-wtypesbase-rpc_imp_level
- KB5004442 / CVE-2021-26414 DCOM hardening — https://support.microsoft.com/en-us/topic/kb5004442-manage-changes-for-windows-dcom-server-security-feature-bypass-cve-2021-26414-f1400b52-c141-43d2-941e-979ed901b716

---

**Research confidence & loose ends:**
- HIGH confidence: all CLSIDs/IIDs quoted (verified against primary sources or multiple corroborating sources), DiagHub/USO/Trapped-COM workflows (read in full from primary blogs), potato mechanisms and build ranges, CVE mappings (2015-2370, 2017-0100, 2017-0298, 2017-0213/0214, 2019-0555, 2019-1405/1322, 2020-1362, 2023-21746).
- MEDIUM confidence (marked in-line where used): PrintSpoofer's current per-build patch status (was unpatched at release; later spooler hardening varies — "verify on target build"); SweetPotato's exact internal method list per release version; GodPotato's internal description (author says "rpcss OXID defects," some third parties mischaracterize it as pure named-pipe — use the DCOM-callback family description shared with DCOMPotato/PrintNotifyPotato).
- The two GPZ blogspot URLs that failed automated fetch (DiagHub blog, PrintSpoofer blog) were verified via their projectzero.google mirror / extensive secondary sources respectively; all technical claims quoted above come from the full primary text where accessible.
