62 KiB
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}withLocalServer32or with anAppIDthat names a Windows service. The server process frequently runs asNT AUTHORITY\SYSTEM,LOCAL SERVICE, orNETWORK 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 includesExecuteLocal 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:
- Process-wide, once:
CoInitializeSecurity(pSecDesc, cAuthSvc, asAuthSvc, pReserved1, dwAuthnLevel, dwImpLevel, pAuthList, dwCapabilities, pReserved3). - Per-proxy (what matters for exploitation):
CoSetProxyBlanket(pProxy, dwAuthnSvc, dwAuthzSvc, pServerPrincName, dwAuthnLevel, dwImpLevel, pAuthInfo, dwCapabilities)— a wrapper forIClientSecurity::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:
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)
- No impersonation at all — server does file/registry/process ops as SYSTEM on attacker-controlled paths (the classic privileged-file-operation bug class).
- Late/short-lived impersonation — server impersonates, checks access, then
RevertToSelf()and performs the write as SYSTEM (the StorSvcSvcMoveFileInheritSecuritybug used in the DiagHub chain). - 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. - Cloaking confusion — static vs dynamic cloaking picks the wrong token when the server itself makes outbound calls (relevant to relay/reflection chains).
- Cross-session / "interactive user" activation — AppID
RunAs = Interactive Userobjects instantiated from session 0 or another user's session (Session Moniker chain, CVE-2017-0100). - 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(IID2D2AC45D-03BB-4B8A-8EFE-93EF98217054),IStandardCollectorService(IID0D8AF6B7-EFD5-4F6D-A834-314740AB8CAA), proxy CLSID4323664B-B884-4929-8377-D2FD097F7BD3. IStandardCollectorServicehas 8 methods (3IUnknown+ 5):CreateSession,GetSession,DestroySession,DestroySessionAsync,AddLifetimeMonitorProcessIdForSession(names recovered via OleViewDotNet IPID→VTable workflow + WinDbgdqs 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:
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):
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)
- Storage Service (
StorSvc) RPC methodSvcMoveFileInheritSecurityimpersonates the caller,MoveFileEx()s a file, then reverts impersonation and callsSetNamedSecurityInfo()as SYSTEM to apply inherited ACEs. Two bugs: failed-ACL revert allows arbitrary file creation (issue 1427); and via hard links,SetNamedSecurityInfoapplies attacker-chosen ACEs to any file SYSTEM canWRITE_DAC(issue 1428). - Pick a non-TrustedInstaller-owned, non-critical System32 file — the blog uses
C:\Windows\System32\license.rtf(enumerate candidates with NtObjectManager'sGet-AccessibleFile -AccessRights WriteDacrun against the StorSvc process token). - Overwrite
license.rtfwith a DLL implementingDllGetClassObject. - DiagHub
CreateSession→AddAgent("license.rtf", …)→ DLL loaded as SYSTEM. - (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
ProcessImageLoadPolicyrestricting 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
VerifyTrustsigning-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 DLLwindowscoredeviceinfo.dllfrom 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 clientusoclient.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}; serverusosvc.dll. CoCreateInstancerequests IIDIUpdateSessionOrchestrator={07F3AFAC-7C8A-4CE7-A5E0-3D24EE8A77E0}; its 5 methods pastIUnknown:CreateUpdateSession,GetCurrentActiveUpdateSessions,LogTaskRunning,CreateUxUpdateManager,CreateUniversalOrchestrator(recovered fromusosvc.dllVTable).IUsoSessionCommonIID ={FCCC288D-B47E-41FA-970C-935EC952F4A4}— 68 methods; OleViewDotNet auto-generated the interface code. Another GUID seen in aQueryInterface:C57692F8-8F5F-47CB-9381-34329B40285A.- Reconstructed "StartScan" client flow (verbatim from part 2):
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 TowerIDNCACN_IP_TCPand a stringhost[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 localAcceptSecurityContext()loop → negotiates a SYSTEM impersonation token locally (or, in Forshaw's original PoC, reflects the auth to the local DCOM activation service to activateCLSID_Package {F20DA720-C02F-11CE-927B-0800095AE340}as SYSTEM; a variant wrote a file toC:\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 Controlobject-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-craftedIStoragewhose string bindings point at127.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 bothCreateProcessWithTokenWandCreateProcessAsUser. 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/):
- COM trigger (COM):
CoGetInstanceFromIStoragewith BITS CLSID{4991d34b-80a1-4291-83b6-3328366b9097}(default;-cto override),IStoragestring 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). - 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). - Fake OXID resolver (COM): implements
IObjectExporter— answersServerAlive2withRPC_S_OK, and forgesResolveOxid2to return aDUALSTRINGARRAYwith TowerIDncacn_npand endpoint stringlocalhost/pipe/roguepotato[\pipe\epmapper]plus security bindings namingNT AUTHORITY\NETWORK SERVICE. (First attempt withncacn_ip_tcp:localhost[9998]+IRemUnknown2+RpcImpersonateClient()yielded only an Identification token — dead end; the named-pipe transport was the fix.) - 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\epmappersuffix; the/pipe/roguepotatoprefix redirects it). - Impersonation (not COM):
ImpersonateNamedPipeClient()→ NETWORK SERVICE impersonation token with the rpcss logon-session LUID. - 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\spoolssdespite\pipe\spoolssbeing 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 (
-cCLSID default BITS{4991D34B-…},-mmethod). 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 — avoidsRpcServerUseProtseqEp(which would hold the port) and, per the authors, relaxes requirements vsRpcImpersonateClient(). 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.
- Trigger (COM):
CoGetInstanceFromIStoragewith 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 SPNcifs/127.0.0.1, causing the privileged DCOM client to initiate NTLM withISC_REQ_UNVERIFIED_TARGET_NAMEand that SPN. - 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").
- 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). - Weaponization: write
SprintCSP.dllto System32 + triggerSvcRebootToFlashingMode(StorSvc RPC, BlackArrowSec's RpcClient — released 2023-02-13), or overwriteprintconfig.dlland instantiate PrintNotify (the authors' Server 2022 demo), or XPS Print Job / NetMan DLL hijack. - Patch: Jan 2023 (CVE-2023-21746):
SsprHandleChallengeMessage— ifISC_REQ_UNVERIFIED_TARGET_NAMEset and SPN present → SPN nulled → SMB anti-reflection deniescifs/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)
whoami /priv→ SeImpersonatePrivilege or SeAssignPrimaryTokenPrivilege present? (No → not a potato target.)- OS ≤ Win10 1803 / Server 2016 → RottenPotatoNG / JuicyPotato (BITS or list CLSID).
- 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).
- Spooler up, Win10/2016-2019 → PrintSpoofer (C#: BadPotato). Spooler down/hardened → EfsPotato / CoercedPotato coercion alternatives.
- Need file-write primitive on ≤ Jan-2023 builds → LocalPotato (+ StorSvc/PrintNotify weaponization); patched → LocalPotato HTTP/WebDAV edition.
- .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:
- 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
QueryInterfaces the legacyIXMLDOMDocument→ XSLT script execution → sandbox escape. - 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). - Abusing the platform's object-instantiation plumbing — coerce a path to
CoCreateInstancewith 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
IDispatchmust vend type info: client callsIDispatch::GetTypeInfo→ITypeInfo, itself marshaled by reference → trapped in server. ITypeInfo::Invokeis not remotable, butITypeInfo::CreateInstanceIS — and it callsCoCreateInstancein the server process. The type info must describe a CoClass (carries a CLSID); reach it viaITypeInfo::GetContainingTypeLib→ITypeLib→ enumerate classes → including referenced type libraries (stdoleis virtually always referenced).- Worked example (verbatim methodology from the post, OleViewDotNet PowerShell module):
Get-ComClass -Service→ enumerate interfaces → filterIsDispatch→ 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 withGet-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: navigateGetRefTypeOfImplType(0)→GetRefTypeInfo→GetContainingTypeLib→GetTypeInfoOfGuid(<StdFont CLSID>)→CreateInstance()→Get-ComObjRefproves 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) viaNtGetCachedSigningLevel/NtSetCachedSigningLevel; raw (non-PE) TLBs fail withSTATUS_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:
- Set
HKLM\SOFTWARE\Microsoft\.NETFramework\AllowDCOMReflection = 1(DWORD) — re-enables .NET DCOM reflection disabled since MS14-009; alsoOnlyUseLatestCLR = 1to 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.) TreatAshijack: pointHKLM\SOFTWARE\Classes\CLSID\{0BE35203-8F91-11CE-9DE3-00AA004BB851}\TreatAs(StdFont) at the .NETSystem.ObjectCOM class.- Create
WaaSRemediationAgentOOP → walk IDispatch→stdole→StdFontCreateInstance→ CLR loads inside the PPL and returnsSystem.Object. 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.- Windows 11 24H2 wrinkle: 64-bit
mscorlib.tlbis a raw TLB → failsVerifyTrustsigning-level (STATUS_INVALID_IMAGE_FORMAT); the 32-bitFramework\v4.0.30319\mscorlib.tlbis 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/OnlyUseLatestCLRvalues, 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)
- 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/UPnPContainerManager64implementing undocumentedIUPnPContainerManager(IID6D8FF8D4-730D-11D4-BF42-00B0D0118B56; methodsReferenceContainer,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
SERVICEgroup 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.
- CVE-2019-1405 — logical flaw in UPnP Device Host service COM objects
- COM Aggregate Marshaler & type-library bugs (Forshaw, May 2017): CVE-2017-0213 (Aggregate Marshaler
Finish_RemQIAndUnmarshal2; PoC uses BITSSetNotifyInterface→ 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). - 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. - WalletService memory corruption over COM — CVE-2020-1362 (Q4n/Haoran Qin + Zhiniang Peng, Jul 2020): WalletService COM server (
Wallet::WalletCustomProperty::SetGroupOOB write →SetLabelBSTR pointer write-what-where → fake vtable → redirect virtual call todxgi!ATL::CComObject<CDXGIAdapter>::vector deleting destructorwhoseLoadLibraryExWuses 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. - 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."
- .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.Typeover DCOM → arbitrary reflection; fixed by MS14-009, re-enablable viaAllowDCOMReflection;OnlyUseLatestCLRselects CLR v4). Pattern: registry values + TreatAs + .NET class = unsigned code in signed processes. - 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.
- 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."
- 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).
- GUI:
- Raw registry:
HKLM\SOFTWARE\Classes\CLSID,…\AppID\{guid}valuesLaunchPermission,AccessPermission,RunAs,Service,ServiceProtectionLevel; machine defaults underHKLM\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
- OOP server (
LocalServer32or 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:IDispatchpresent (type-library vector), .NET interop,RunAs=Interactive User.
8.3 Static RE checklist (Forshaw's DiagHub recipe, generalized)
- OleViewDotNet: create instance → Marshal ⇒ View Properties → capture PID + IPID + OXID + string bindings + authn services.
- 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). - WinDbg attach:
dqs <module>+<offset> L<nMethods>→ symbolized method names (public symbols on MS components). - OVDN View Proxy Definition → decompiles NDR stubless bytecode → method signatures; Structures tab → marshaled struct layouts → rebuild the interface in C++/C#.
- In IDA/Ghidra: xref command strings (itm4n's
"StartScan"technique), map_guard_dispatch_icall_fptrindirect calls to VTable slots via dynamic breakpoints; match client proxy slots (ObjectStublessClientN) to server symbols. - Hunt the failure modes: missing/late
CoImpersonateClient/RpcImpersonateClient, earlyRevertToSelf, identify-level acceptance, device-map/symlink handling under impersonation,RunAsmismatches, 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)
- Impersonation Contract sequence diagram (d06/d12).
- DiagHub chain (d13): StorSvc
SvcMoveFileInheritSecurity+ hard link →WRITE_DAConlicense.rtf→ overwrite →CoCreateInstance({42CBFAA7-…})→ CreateSession → AddAgent → LoadLibraryExW as SYSTEM; mitigation ribbon: 19H1 ProcessImageLoadPolicy. - RoguePotato chain (d15): swimlane with COM lanes (1–4) vs pipe/token (5–7).
- Potato decision tree (d16 timeline + text tree): color-code COM trigger vs RPC coercion vs NTLM/LSASS; build ranges per branch.
- Trapped COM object flow (d17): IDispatch→ITypeInfo→stdole→StdFont CreateInstance→TreatAs→System.Object in PPL→Assembly.Load; VerifyTrust side panel.
- 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.