Skip to content
KitploitKITPLOIT
工具博客
Log in
提交
工具博客
提交

黑客、渗透测试和网络安全工具,武装您的安全武器库!

Kitploit 是一个黑客、网络安全和渗透测试工具的目录。发现最新的项目更新,查找漏洞、分析系统、自动化测试并加强你的安全。

··订阅源·联系·隐私·© 2026 Kitploit

工具目录

分类

查看所有分类
Loading categories
CVE-2025-21298 — 一个严重的 Windows OLE 零点击漏洞。这是针对 CVE-2025-21298 – Windows OLE 远程代码执行漏洞(CVSS 9.8)的概念验证。这是一个内存损坏 PoC。 | Kitploit
工具/GitHubGitHub/thebl4ckph4nt0m/cve-2025-21298
内存取证漏洞分析漏洞利用二进制利用
GitHubthebl4ckph4nt0m/cve-2025-21298

CVE-2025-21298

一个严重的 Windows OLE 零点击漏洞。这是针对 CVE-2025-21298 – Windows OLE 远程代码执行漏洞(CVSS 9.8)的概念验证。这是一个内存损坏 PoC。

查看仓库
1111年前尚未审核

最受欢迎

查看全部 →

发现我们社区最常用的工具。

探索所有工具

浏览我们的工具集合

查看所有工具 →
分享

CVE-2025-21298

内容

这是 CVE-2025-21298 - Windows OLE 远程代码执行漏洞(CVSS 9.8) 的概念验证。这是一个内存损坏 PoC,而非漏洞利用。

漏洞

该漏洞位于 ole32.dll!UtOlePresStmToContentsStm 中。此函数的作用是将 OLE 存储中 "OlePres" 流里的数据转换为适当格式的数据,并插入同一存储中的 "CONTENTS" 流。它接收一个指向存储对象的 IStorage 指针,以及三个不太重要的参数。

下面我们可以看到该函数与 2025 年 1 月补丁的差异:

__int64 __fastcall UtOlePresStmToContentsStm(IStorage *pstg, wchar_t *puiStatus, __int64 a3, unsigned int *lpszPresStm)
{
  struct IStorageVtbl *lpVtbl; // rax
  int v7; // r14d
+ bool IsEnabled; // al
  IStream *v10; // rcx
  bool v11; // zf
  struct IStorageVtbl *v12; // rax
  int v13; // ebx
  HRESULT v14; // eax
  const wchar_t *v15; // rdx
  IStream *pstmContents; // [rsp+40h] [rbp-19h] BYREF
  IStream *pstmOlePres; // [rsp+48h] [rbp-11h] BYREF
  tagFORMATETC foretc; // [rsp+50h] [rbp-9h] BYREF
  tagHDIBFILEHDR hdfh; // [rsp+70h] [rbp+17h] BYREF

  *lpszPresStm = 0;
  lpVtbl = pstg->lpVtbl;
  pstmContents = 0LL;
  v7 = 1;
  // Create a "CONTENTS" stream in the storage and store it into pstmContents
  if ( (lpVtbl->CreateStream)(pstg, L"CONTENTS", 18LL, 0LL, 0, &pstmContents) )
    return 0LL;
  // Immediately release pstmContents, we're not going to be using it right now
  (pstmContents->lpVtbl->Release)(pstmContents);
+ IsEnabled = wil::details::FeatureImpl<__WilFeatureTraits_Feature_3047977275>::__private_IsEnabled(&`wil::Feature<__WilFeatureTraits_Feature_3047977275>::GetImpl'::`2'::impl);
+ v10 = pstmContents;
+ v11 = !IsEnabled;
  v12 = pstg->lpVtbl;
+ if ( !v11 )
+   v10 = 0LL;
+ pstmContents = v10;
  (v12->DestroyElement)(pstg, L"CONTENTS");
  v13 = (pstg->lpVtbl->OpenStream)(pstg, &OlePres, 0LL, 16LL, 0, &pstmOlePres);// 2nd option to fail -> no OlePres stream
  if ( v13 )
  {
    *lpszPresStm |= 1u;
    if ( (pstg->lpVtbl->OpenStream)(pstg, L"CONTENTS", 0LL, 16LL, 0, &pstmContents) )
    {
      *lpszPresStm |= 2u;
    }
    else
    {
      (pstmContents->lpVtbl->Release)(pstmContents);
+     wil::details::FeatureImpl<__WilFeatureTraits_Feature_3047977275>::__private_IsEnabled(&`wil::Feature<__WilFeatureTraits_Feature_3047977275>::GetImpl'::`2'::impl);
    }
    return v13;
  }
  foretc.ptd = 0LL;
  v13 = UtReadOlePresStmHeader(pstmOlePres, &foretc, 0LL, 0LL);
  if ( v13 >= 0 )
  {
    v13 = (pstmOlePres->lpVtbl->Read)(pstmOlePres, &hdfh, 16LL);
    if ( v13 >= 0 )
    {
      v13 = OpenOrCreateStream(pstg, L"CONTENTS", &pstmContents);
      if ( v13 < 0 )
      {
        *lpszPresStm |= 2u;
        goto $errRtn_197;
      }
      if ( foretc.dwAspect == 4 )
      {
        *lpszPresStm |= 4u;
        v7 = 0;
        v13 = 0;
        goto $errRtn_197;
      }
      if ( foretc.cfFormat == 8 )
      {
        v14 = UtDIBStmToDIBFileStm(pstmOlePres, hdfh.dwSize, pstmContents);
LABEL_19:
        v13 = v14;
        goto $errRtn_197;
      }
      if ( foretc.cfFormat == 3 )
      {
        v14 = UtMFStmToPlaceableMFStm(pstmOlePres, hdfh.dwSize, hdfh.dwWidth, hdfh.dwHeight, pstmContents);
        goto LABEL_19;
      }
      v13 = -2147221398;
    }
  }
$errRtn_197:
  if ( pstmOlePres )
    (pstmOlePres->lpVtbl->Release)(pstmOlePres);
  // Release pstmContents if it still exists, we need to clean up
  if ( pstmContents )
    (pstmContents->lpVtbl->Release)(pstmContents);
  if ( foretc.ptd )
    CoTaskMemFree(foretc.ptd);
  if ( v13 )
  {
    v15 = L"CONTENTS";
    goto LABEL_31;
  }
  if ( v7 )
  {
    v15 = &OlePres;
LABEL_31:
    (pstg->lpVtbl->DestroyElement)(pstg, v15);
  }
  return v13;
}

问题出在 pstmContents 变量上。最初它用于保存函数开头创建的 "CONTENTS" 流对象的指针。该流在创建后立即被销毁,存储在 pstmContents 中的指针也会被释放(在 coml2.dll!ExposedStream::~ExposedStream 中释放)。然而,该变量仍然包含已释放的指针。在函数后面,该变量可能会被重新用来保存 "CONTENTS" 流的指针——因此,函数末尾有清理代码,如果指针存放在该变量中,就会释放它。但代码没有考虑到 UtReadOlePresStmHeader 可能会失败——如果失败,pstmContents 仍将指向已释放的指针,并且我们会进入清理代码,再次释放该指针。于是就会发生双重释放。

从补丁差异中可以看出,微软通过在最初包含的指针被释放后将 pstmContents 置零来修复此问题。

复现

仓库中有一个可复现该漏洞的 RTF 文件。我通过用 MS Word 打开该文件进行了测试,你也可以用其他能解析 RTF 数据的应用程序(例如 Outlook)进行测试。通过其他嵌入 OLE 对象的格式进行利用也许可行,但我没有尝试过。

视频:

poc

下载工具