Pyinstaller, for those of you who aren’t aware, is a useful program that “converts (packages) Python programs into stand-alone executables”. This is great for the distribution of Python-based projects, as a developer doesn’t have to rely on Python already being installed on a user’s system. A few years ago, the security community started to realize that Pyinstaller could be repurposed to distribute malicious binaries during pentests. Debasish Mandal’s post describes how to use Python to inject shellcode into memory, and David Kennedy’s “Pyinjector” tool used these concepts, along with Pyinstaller, to package up a nice shellcode-injecting executable. The Veil-Framework has several Python-based payloads that also utilize Pyinstaller (natively on Kali linux) to easily package up malicious payloads into deployable binaries.
Since Pyinstaller uses a compressed container to zip up the targeted Python script, this approach has ended up being quite effective at evading antivirus solutions, as it basically acts as a legitimate packer for malicious logic. However, some solutions have started to flag on the Pyinstaller loader that unpacks and invokes the target Python script. Also, due to issues with the Pyinstaller loader and DEP, some methods for Python shellcode injection will not work when packed into a Pyinstaller exeutable. So a few months ago the Veil-Framework team started investigating this issue, trying to find a possible workaround. There’s a detailed blog post on the Veil-Framework site that describes the solution in detail.
I wanted to go a bit further, and dove into the internals of the Pyinstaller binary to get familiar with its functionality. A produced Pyinstaller binary is essentially a small loader executable with a CArchive attached at the end that contains a compressed Python installation, needed libraries, and your specified Python script. On execution, the loader extracts the environment to disk, loads up necessary libraries, and kicks off script execution. Luckily, I soon found that the Pyinstaller loader used to produce executables could be recompiled on Kali linux, and with dynamic recompilation, why not add some obfuscation?
I started with stripping down the Pyinstaller binary to the minimum functionality for Windows executables- the code for OSX and other platforms could easily go. Everything in the code that could be randomized or shuffled was, randomized library imports were added in, and various processing methods were interspersed throughout the code to complicate the call tree of the program. We can then top everything off with a randomized icon for the executable.
The end result is Pwnstaller 1.0. Every time the code is run:
- Obfuscated code for all source files associated with the Pyinstaller runw.exe launcher are generated
- a randomized icon is chosen for the final packaged result
- mingw32 is used to compile everything into a new runw.exe, all on Kali \m/
- the new runw.exe is copied into the correct resource location to be used by Pyinstaller
Pwnstaller will hopefully extend the lifetime of Veil-Evasion Python payloads by making static signatures reasonably difficult to write. The code is up on Github, and has been incorporated into the development branch of Veil-Evasion– there is now a “2 – Pwnstaller” option on the Python compilation menu (the “–pwnstaller” cli flag has been added as well):
This code will be rolled into the master branch of Veil-Evasion for this month’s V-Day on the 15th.