Process Injection and Migration Theory
In this section, we’ll discuss the basic theory behind
process injection and migration.
By definition, a process is a container
that is created to house a running application.
Each Windows process maintains its own virtual memory space.
Although these spaces are not meant to directly interact with one another,
we may be able to accomplish this with various Win32 APIs.
On the other hand, a thread executes the compiled assembly code of the application.
A process may have multiple threads to perform simultaneous actions
and each thread will have its own stack
and shares the virtual memory space of the process.
As an overview,
we can initiate Windows-based process injection by opening a channel
from one process to another through the Win32 OpenProcess API.
We’ll then modify its memory space through the VirtualAllocEx
and WriteProcessMemory APIs, and finally create a new execution thread
inside the remote process with CreateRemoteThread.
We’ll discuss these APIs in more detail in the next section,
but we need to take a moment to discuss security permissions.
The OpenProcess API opens an existing local process for interaction
and must be supplied with three parameters.
The first argument establishes the access rights we require on that process.
Let’s take a moment to discuss these access rights.
To call OpenProcess successfully,
our current process must possess the appropriate security permissions.
Every process has a Security Descriptor
that specifies the file permissions of the executable
and access rights of a user or group,
originating from the creator of the process.
This can effectively block privilege elevation.
All processes also have an Integrity level
that restricts access to the process.
This works by blocking access from one process to another
that has a higher Integrity level,
however accessing a process with a lower Integrity level is generally possible.
Let’s examine these settings on our Development machine.
First, we’ll execute Notepad as our standard Offsec user.
Then we’ll examine the security setting of the process
by launching the 64-bit version of Process Explorer.
Note that in order to access properties for processes
running at integrity levels higher than medium,
we must launch Process Explorer as a high integrity process.
Once open, we select Notepad,
open the Properties window,
and navigate to the Security tab.
This output details the users and groups that may interact with the process,
as well as the integrity levels.
In this case, this Notepad process runs at Medium Integrity,
which is a standard level for most processes.
We can click the Permissions button to open a new window
showing the specific user permissions.
By selecting the Offsec user,
we find that we have both read and write permissions to the process.
With these settings we should be able to use OpenProcess
to open a handle to the Notepad process
In contrast, if we open Notepad as an administrator
through the Run as administrator feature,
and look at the same Security tab in Process Explorer
for the new Notepad instance,
we find the same set of users and groups have access
but that it is now running as a high integrity level process.
In this case, OpenProcess will fail if we execute it as part of our code
in a Word macro or Jscript file
since the integrity level of the target process will be higher.
In general, we can only inject code into processes running at the same
or lower integrity level of the current process.
This makes explorer.exe a prime target
because it will always exist and does not exit until the user logs off.
Because of this, we’ll shift our focus to explorer.exe.
Now that we have selected a process and know the security level we need,
we can discuss the second and third arguments to OpenProcess.
The second argument determines if the returned handle
may be inherited by a child process,
and the third
specifies the process identifie of the target process.
We’ll discuss the values of these settings in the next section.
Next, we can discuss the VirtualAllocEx API.
In our previous shellcode runner,
we used VirtualAlloc to allocate memory for our shellcode.
Unfortunately, that only works inside the current process
so we must use the expanded VirtualAllocEx API.
This API can perform actions in any process
that we have a valid handle to.
The next API, WriteProcessMemory,
will allow us to copy data into the remote process.
Note that since our previous RtlMoveMemory and C# Copy methods
do not support remote copy, they are not useful here.
Similarly, since CreateThread
does not support the creatio of remote process threads,
we must rely on the CreateRemoteThread API instead.
Now that we’ve introduced these APIs, let’s begin implementing them in C#.