Exploring the basics of remote shellcode injection
Environment Setup
OS: Windows 10
Tools: API Monitor
Target: Running processes with the same integrity level
These are some tools that might be useful.
- API Monitor: http://www.rohitab.com/apimonitor
- Process Hacker: https://processhacker.sourceforge.io/
I’m recently looking into windows injections, and will be demonstrating two basic techniques for shellcode injection.
We’ll start off by using the documented high level Win32 APIs, look at what it does under the hood, then try to move away from them and directly use the undocumented functions within ntdll.dll
.
If there’s a part 2 it’ll probably be one level lower where we do the syscalls directly.
Methodology
- Prompt user for id of process to inject into
- Obtain handle to process
- Allocate RWX region in process’s memory and obtain its address
- Write shellcode into RWX memory
- Create a thread that executes the shellcode
For POC purpose I’ll be using a shellcode that pops notepad.msfvenom -p windows/x64/exec CMD=notepad.exe -f c
The shellcode will just be a huge char array in our code, but if you want to do it the “right” way perhaps you can embed it as a resource in the executable.
I’ll also be using the C language, because C++ is nasty.
Using Win32 API
References:
- OpenProcess: https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-openprocess
- VirtualAllocEx: https://docs.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualallocex
- WriteProcessMemory: https://docs.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-writeprocessmemory
- CreateRemoteThread: https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createremotethread
These documented APIs are pretty self explanatory and clear, so let’s just see the final code.
classic.c
1 |
|
Do note that you must compile as 64bit in order to inject into 64bit processes.
If it doesn’t work for you, try viewing the memory of the process you are injecting shellcode into in Process Hacker in the properties
section to check if RWX region is allocated and if shellcode is written fully.
Assuming it works, let’s attach API Monitor and see exactly what the Win32APIs do.
These are the filters we need.
Attach it to our injection tool and run the injection again.
API Monitor shows that VirtualAllocEx
calls NtAllocateVirtualMemory
, WriteProcessMemory
probably calls NtWriteVirtualMemory
and so on.
Knowing this, we can go ahead and map these functions in ntdll.dll
manually and call them.
Using ntdll functions
We need:
- NtAllocateVirtualMemory
- NtWriteVirtualMemory
- NtCreateThreadEx
Since these APIs are not documented by Microsoft, we need to find some external references made by reverse engineers.
References:
- http://undocumented.ntinternals.net/
- https://docs.rs/ntapi/0.3.6/ntapi/
- https://docs.microsoft.com/en-us/windows/win32/api/ntdef/
Let’s look at the definition of an NTAPI
function from the first reference link.
1 | NTSYSAPI |
NTSTATUS
is the actual return value, while NTSYSAPI
marks the function as a library import and NTAPI
defines the windows api calling convention.
IN
means the function requires it as input, while OUT
means that the parameter passed in is modified with some return output.
When we prototype the functions, we just need to note the NTAPI
part.
In fact you can also use WINAPI
since the both of them resolve to __stdcall
.
1 | typedef NTSTATUS(NTAPI* NAVM)(HANDLE, PVOID, ULONG, PULONG, ULONG, ULONG); |
Here we prototype some function pointers that we’ll map the address of the actual functions in ntdll.dll
to later.
You might notice that some types are also missing, for example the POBJECT_ATTRIBUTES
, so let’s find and define them from the references.
1 | typedef struct _UNICODE_STRING { |
Now we can load ntdll.dll
and map the functions.
1 | HINSTANCE hNtdll = LoadLibraryW(L"ntdll.dll"); |
Finally we can call these functions.
The arguments can also be found in the first reference above.
manual-ntdll.c
1 |
|
Run it and we get code execution.
Conclusion
For the blue team it’s probably quite easy to detect such API calls, as they just have to hook the APIs or even go lower and hook the syscalls. I’ll research more on unhooking APIs with direct system calls in the future and hopefully include that in part 2.
Or maybe I’ll be too busy studying for exams :)
byebye