ulexecve: Execute dynamic or statically compiled ELF Linux binaries without ever calling execve()
ulexecve
This Python tool is called ulexecve and it stands for userland execve. It helps you execute arbitrary ELF binaries on Linux systems from userland without ever calling the execve() systemcall. In other words: you can execute arbitrary binaries directly from memory without ever having to write them to storage. This is very useful from an anti-forensic or red-teaming perspective and enables you to move around more stealthily while still dropping compiled binaries on target machines. The tool works on CPython 3.x as well as CPython 2.7 (and possibly earlier) on the supported Linux platforms (x86, x86-64, and aarch64). Both static and dynamically compiled ELF binaries are supported. Of course, there will always be a small subset of binaries which may not work or result in a crash, and for these, a 100% reliable fallback method is implemented on top of the modern memfd_create() system call.
Background
Linux userland execve tools have a history that goes back roughly two decades. The first solid writeups on this were made by the grugq in The Design and Implementation of Userland Exec [1] as well another article in Phrack 62 [2]. Anti-forensic techniques to execute binaries directly from memory are fairly standard. Rapid7’s mettle for example has a library named libreflect which includes a utility noexec which also attempts to execute an ELF via reflection only. However, this tool is written in C and it has the implicit requirement that you need to transfer the noexec binary on the target system as well as being able to execute this binary.
In modern container environments, this is definitely not always possible anymore. However, a lot of container environments do contain a Python installation. Having the ability to simply download a Python script via curl or so on a target machine and then being able to execute this script to then stealthily execute arbitrary binaries is very useful from an anti-forensic perspective.
This is also the reason the tool is all implemented in just one file. This should make it easier to download it on target systems and not have to worry about installing any other dependencies before being able to run it. The tool is tested with Python 2.7 even though this Python version is deprecated. There are many systems still out there with 2.x versions so this is useful.
No good other implementations of a Python userland execve() existed. There is SELF [3] which was not extensively documented, lacked easy debugging options but more importantly didn’t work at all. The ulexecve implementation was written from scratch. It parses the ELF file, loads and parses the dynamic linker as well (if needed), maps all segments into memory, and ultimately constructs a jump buffer containing CPU instructions to ultimately transfer control from the Python process directly to the newly loaded binary.
All the common ELF parsing logic, setting up the stack, mapping the ELF segments, and setting up the jump buffers is abstracted away so it is fairly easy (in the order of a couple of hours) to port to another CPU. Porting it to other ELF-based platforms such as the BSDs might be a bit more involved but should still be fairly straightforward. For more information on to do so just check the comments in the code.
Please note that it is an explicit design goal to have no external dependencies and to have everything implemented in a single source code file. If you need to make smaller payloads it should be fairly trivial to remove support for certain CPU types or rip out all the debug information and other options.
Install & Use
Copyright (c) 2021-2022, Anvil Secure Inc.