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 propertiessection 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