Injecting Python Code Into Native Processes

There is quite a bit of material publicly available on DLL injection, the different techniques and how it works. Often times it’s helpful for a researcher to be able to execute code within the context of a specific process, and DLL injection is an ideal way to accomplish this. By injecting a DLL into another process, that process can be “infected” allowing the injected DLL to do anything the application itself could do. This technique usually means that the attacker or researcher needs to write their DLL in C to accomplish the task at hand. As an alternative approach, this post will outline how Python can be injected into processes which allows this rich scripting language to be executed within the context of the targeted process, thus providing an alternative language for injection to C or C++.

At a high level, the technique involves the following steps:

  1. Find and inject the Python DLL into the target process using CreateRemoteThread and LoadLibrary
  2. Initialize Python in the target process
  3. Execute arbitrary Python code in a new thread

Python provides functionality for embedding itself into other applications. This is generally how non-Python programs offer support for plugins and scripts implemented in Python. Applications that choose to embed Python load the Python interpreter and call the native functions that the module exports. To inject the Python interpreter into another process, one needs to simulate this at runtime by first injecting the Python DLL into the targeted process, which effectively causes the process to have Python “embeded” in it. No special techniques are required for this step; the standard DLL injection techniques using CreateRemoteThread and LoadLibrary are sufficient. On Windows, Python 2.7 provides a DLL at the path C:\Windows\system32\python27.dll which exports the functions necessary to use the embedded interpreter.

Once the Python DLL is injected in the target process, the functions exported by it that are used to embed Python become available. The easiest way to run these and other remote functions is by resolving their addresses and calling CreateRemoteThread with the address of the function to call. Due to CreateRemoteThread only passing one argument to the target function, only functions which require one argument can be called directly in this fashion. The Python embedded API does however provide the only two functions necessary to initialize the interpreter and run code requiring exactly one argument each. The two functions which can accomplish this are Py_InitializeEx and PyRun_SimpleString.

After the Python DLL is loaded into the target process, Py_InitializeEx must be called to initialize the interpreter. The Py_InitializeEx version is preferred over the Py_Initialize version as it allows a single argument to be passed to it. The single argument of initsigs can be used to optionally initialize signal handlers (which is not necessary in most cases and can be left as 0).

Once the interpreter has been initialized, arbitrary Python code can be executed using the PyRun_SimpleString function. This function also takes a single argument which is the address of the location in memory where the code to execute is stored. Therefore, it is necessary to allocate space for the code to execute and write it into the target process. This technique is often used by malware to write native shellcode into a process before calling CreateRemoteThread on it for it’s execution. In this case, the code that is written into memory is Python code and is passed in as the single argument to PyRun_SimpleString. The thread will continue to run as long as the Python code is executing.

The mayhem library now includes the script below which can be used to demonstrate this technique, combining all of the steps outlined above. This script requires that Python be installed on the system and takes advantage of the mayhem library which allows easy access for runtime manipulation of processes. The mayhem library is used for the initial library injection, as well as the allocation and copying of necessary memory.

As a demonstration this tool can be used to inject a Python Meterpreter from the Metasploit Framework into the calc process.

Python Meterpreter Injected In To Calc.exe

Python Meterpreter Injected In To Calc.exe