Files
An0nUD4Y-Offensive-COM/research/R3-privesc.md
T
2026-07-18 21:50:11 +05:30

481 lines
62 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 | Win811, Server 20122022 |
| **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 20122022 |
| **PrintNotifyPotato** | BeichenDream, late 2022 | PrintNotify CLSID + fake `IUnknown`/pointer-moniker callback; works even with legacy Spooler disabled | **Yes** | Win10/11, Server 20122022 |
| **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 20122022 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 20122019 |
| **BadPotato** | BeichenDream | itm4n's PrintSpoofer ported to C# | No | Win810, 20122019 |
| **CandyPotato** | klezVirus | Pure C++ weaponized RottenPotatoNG | Yes (Rotten lineage) | Win10/2016 era |
| **MultiPotato** | S3cur3Th1sSh1t | Multi-trigger combo | mixed | — |
| **SigmaPotato** | tylerdotrar, 202324 | Modernized GodPotato-fork style; `--revshell`, in-memory `[SigmaPotato]::Main()` via .NET reflection | Yes (DCOM lineage) | Win811, 20122022 |
| **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 18031903. 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 12; 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 d12d17)
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 (14) vs pipe/token (57).
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.