Back to Blog
Offensive Security

Dangling COM Registrations: How a Missing DLL Became a Windows SYSTEM Exploit

Google Project Zero's James Forshaw detailed CVE-2026-66804, an incomplete fix for the 'Dark Elevator' bug. A COM class registered system-wide but pointing at a DLL that did not exist let any local user plant code and run it as SYSTEM.

PyramidLedger Research4 min read
Share

Key Takeaways

  • CVE-2026-66804 is an incomplete fix for CVE-2026-50343, the 'Dark Elevator' bug documented by Calif, and it allowed local privilege escalation to SYSTEM.
  • The root cause was a dangling COM registration. The CrossDevice CLSID pointed to a DLL under %PROGRAMDATA%, a location ordinary users can write to, and the DLL was missing.
  • The exploit sends a custom-marshaled object to a SYSTEM COM service that permits custom marshaling, which makes COM load the planted DLL.
  • Defenders and researchers can audit for the same class of bug by enumerating CLSIDs whose in-process server DLL cannot be loaded and checking whether the missing path is user-writable.

What was disclosed

James Forshaw of Google Project Zero published a technical write-up of a Windows privilege escalation, CVE-2026-66804, which he and 14 others reported and Microsoft has since fixed. It is an incomplete fix for CVE-2026-50343, a bug Calif dubbed 'Dark Elevator'. According to the post, the issue stayed exploitable until the CVE-2026-66804 fix shipped, and the earlier fix for the InstallService path was not sufficient.

The root cause: a registration with no server

A COM registration normally has two parts: a server binary (for in-process components, a DLL) and a CLSID entry under the HKEY_CLASSES_ROOT registry key that points to it. Here the second part existed without the first. The CrossDevice class, CLSID {E9F83CF2-E0C0-4CA7-AF01-E90C70BEF496}, was registered in the system-wide classes key, so every user and system service could resolve it. Its in-process server was set to %PROGRAMDATA%\CrossDevice\CrossDevice.Streaming.Source.dll, and that file was not present.

Because C:\ProgramData is writable by ordinary users, any local user could create the CrossDevice directory and drop a DLL with the expected name. The registration was trusted system-wide, but the path it named was controlled by the lowest-privileged user on the machine.

Turning a planted DLL into SYSTEM

A planted DLL is only useful if a privileged process loads it. The post's approach is to send a custom-marshaled object, an OBJREF that names the dangling CLSID, to a privileged COM service. During unmarshaling, the COM runtime instantiates that class and loads the attacker's DLL in the privileged process.

Many privileged processes block custom marshaling by default, so the target matters. The post uses the Shell Create Object Handler service, which runs as SYSTEM and allows custom marshaling. It cannot be instantiated directly. The post describes starting it through the \Microsoft\Windows\Shell\CreateObjectTask scheduled task, which normal users can start, then signalling a named event, ShellCreateObjectTaskReadyEvent. The exploit then delivers the marshaled object through ICreateObject using CoGetInstanceFromIStorage. The result is code execution as NT AUTHORITY\SYSTEM.

Why this matters

  • Local privilege escalation chains. An attacker with a foothold as a standard user, for example through phishing or a compromised application, can use a bug like this to take full control of the host.
  • Incomplete fixes recur. The same underlying feature was already fixed once as CVE-2026-50343. Patching one path while leaving the dangling registration reachable another way is a common pattern worth testing for during retests.
  • The bug class is auditable. Nothing here relies on memory corruption. It is a configuration-level flaw: a trusted registration whose target is missing and writable.

Defensive and testing takeaways

The post does not offer formal mitigation advice beyond the fix, but it points to useful controls and techniques. Applying Microsoft's update for CVE-2026-66804 is the direct remediation. For developers of COM servers, custom marshaling can be restricted with EOAC_NO_CUSTOM_MARSHAL in CoInitializeSecurity, or with IGlobalOptions::Set using COMGLB_UNMARSHALING_POLICY. For researchers, the author describes a PowerShell script built on his OleViewDotNet and NtObjectManager modules that finds CLSIDs whose DLL cannot be loaded, so the missing paths can be checked for user-writable locations such as %PROGRAMDATA%.

Security teams should treat installers and uninstallers as part of the attack surface. Software that removes a binary but leaves its registration behind, or registers a path outside protected directories, can create this condition.

Frequently Asked Questions

What is a dangling COM object registration?

It is a COM class registered under HKEY_CLASSES_ROOT whose server binary, normally a DLL for in-process components, is missing. If the expected path is writable by low-privileged users, an attacker can supply their own DLL and have COM load it.

How does CVE-2026-66804 relate to 'Dark Elevator'?

According to Google Project Zero, CVE-2026-66804 is an incomplete fix for CVE-2026-50343, the bug Calif named 'Dark Elevator'. Both involve the CrossDevice COM registration, and the later fix closed the remaining exploitable path.

How can teams look for similar issues?

Enumerate COM CLSIDs whose in-process server DLL fails to load, then check whether the referenced path sits in a user-writable location. The Project Zero post describes a PowerShell script using OleViewDotNet and NtObjectManager for this.

Sources

  1. 1Windows Exploitation Techniques: Dangling COM Object Registrations — Google Project Zero
Share
Newsletter

Get new posts by email

Security and AI research from our team, when we publish. No spam, unsubscribe anytime.

By subscribing you agree to receive our blog emails. See our privacy policy.

Read next