
एक गंभीर Windows OLE ज़ीरो-क्लिक भेद्यता। यह CVE-2025-21298 - Windows OLE रिमोट कोड एक्ज़ीक्यूशन भेद्यता (CVSS 9.8) के लिए एक प्रूफ-ऑफ-कॉन्सेप्ट है। यह एक मेमोरी करप्शन PoC है।
यह CVE-2025-21298 - विंडोज OLE रिमोट कोड निष्पादन भेद्यता (CVSS 9.8) के लिए एक प्रूफ-ऑफ-कॉन्सेप्ट है। यह एक मेमोरी करप्शन PoC है, कोई एक्सप्लॉइट नहीं।
भेद्यता ole32.dll!UtOlePresStmToContentsStm में स्थित है। फ़ंक्शन का उद्देश्य OLE स्टोरेज के अंदर "OlePres" स्ट्रीम में डेटा को उपयुक्त रूप से फ़ॉर्मेट किए गए डेटा में बदलना और इसे उसी स्टोरेज में "CONTENTS" स्ट्रीम में डालना है। यह एक स्टोरेज ऑब्जेक्ट के लिए IStorage पॉइंटर और तीन कम महत्वपूर्ण आर्गुमेंट प्राप्त करता है।
नीचे हम जनवरी 2025 पैच के अंतर के साथ फ़ंक्शन का कार्यान्वयन देख सकते हैं:
__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 ऑब्जेक्ट एम्बेड करने वाले अन्य प्रारूपों के माध्यम से शोषण संभव हो सकता है, मैंने प्रयास नहीं किया।
वीडियो: