POC详情: f7ad0e3ca9e4f67a242205b65a4ecad4e2bcbeec

来源
关联漏洞
标题: Microsoft OLE 资源管理错误漏洞 (CVE-2025-21298)
描述:Microsoft OLE是美国微软(Microsoft)公司的一种面向对象的技术。 Microsoft OLE存在资源管理错误漏洞。攻击者利用该漏洞可以远程执行代码。以下产品和版本受到影响:Windows Server 2019 (Server Core installation),Windows Server 2022,Windows Server 2022 (Server Core installation),Windows 10 Version 21H2 for 32-bit Systems,Wi
描述
Proof of concept & details for CVE-2025-21298
介绍
# content

This is a proof-of-concept for [CVE-2025-21298 - Windows OLE Remote Code Execution Vulnerability (CVSS 9.8)](https://msrc.microsoft.com/update-guide/vulnerability/CVE-2025-21298). This is a **memory corruption PoC**, not an exploit.

Full patch diff via [ghidriff](https://github.com/clearbluejar/ghidriff): [LINK](diff/ole32_dec24.dll-ole32_jan25.dll.ghidriff.md)

# vulnerability

The vulnerability is located in `ole32.dll!UtOlePresStmToContentsStm`. The purpose of the function is ti convert data in an "OlePres" stream within an OLE storage into appropriately formatted data and insert it into the "CONTENTS" stream in the same storage. It receives an `IStorage` pointer to a storage object and three rather unimportant arguments.

Below we can see the implementation of the function with a diff from the Jan 2025 patch:

```diff
__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;
}
```

The problem is in the `pstmContents` variable. Initially it's used to store the pointer to the "CONTENTS" stream object that's created at the beginning of the function. The stream is immediately destroyed after being created and the pointer stored in `pstmContents` is released (which frees it in `coml2.dll!ExposedStream::~ExposedStream`). However, the variable still contains the free'd pointer. Further down in the function, the variable may be reused to store the pointer to the "CONTENTS" stream again - because of this, there's cleanup code at the end of the function that releases the pointer in case it's stored in the variable. The code fails to account for the fact that `UtReadOlePresStmHeader` may fail - if that happens, `pstmContents` will still point towards the free'd pointer and we'll fall through to the cleanup code, which will release the pointer again. As such, a double-free situation will happen.

As can be seen in the patch diff, Microsoft fixed the issue by setting `pstmContents` to zero after the pointer it contains initially is released.

# Reproducing

In the repo is an rtf file which reproduces the vulnerability. I tested by opening the file in MS Word but you can also test it with other applications that parse RTF data (e.g. outlook). Exploitation through other formats which embed OLE objects may be possible, I haven't tried.

Video: 

[poc.webm](https://github.com/user-attachments/assets/2acc7ea4-f6cb-402b-9148-528535d94042)

# Details

I will publish details later.

文件快照

[4.0K] /data/pocs/f7ad0e3ca9e4f67a242205b65a4ecad4e2bcbeec ├── [4.0K] diff │   └── [ 96K] ole32_dec24.dll-ole32_jan25.dll.ghidriff.md ├── [1.0K] LICENSE ├── [4.0K] poc │   └── [ 221] cve-2025-21298-poc.rtf ├── [5.4K] README.md └── [4.0K] video └── [5.6M] poc.webm 3 directories, 5 files
神龙机器人已为您缓存
备注
    1. 建议优先通过来源进行访问。
    2. 如果因为来源失效或无法访问,请发送邮箱到 f.jinxu#gmail.com 索取本地快照(把 # 换成 @)。
    3. 神龙已为您对POC代码进行快照,为了长期维护,请考虑为本地POC付费,感谢您的支持。