4 ms·
In-Memory-Only ELF Execution Without Tmpfs
- pwdisswordfish2 7y agoOh yeah, I've once discovered this method myself. With execveat() one can even avoid going through procfs. It doesn't work worth shbang-based scripts, though; that just returns ENOENT. Makes sense, I suppose.
- GrayShade 7y agoThere's also fexecve().
- ifoundthetao 7y agoWould you be willing to share how you did that (avoiding procfs too)?
- pwdisswordfish2 7y agoInstead of snprintf(pathbuf, sizeof(pathbuf), "/proc/self/fd/%u", fd); execve(pathbuf, argv, envp); you do execveat(fd, "", argv, envp, AT_EMPTY_PATH); and that's it.
- ifoundthetao 7y agoThanks!!
- ldng 7y agoAnd when would you want to do that ? Really curious as I fail to see a usecase where the same goal could not be achieve otherwise. But there must be some otherwise the API would not have been introduced i guess.
- twoodfin 7y agoOrganizing and executing code via a storage scheme other than a file system? Security issues aside, I can imagine maintaining compiled code for stored procedures in database BLOBs. Similarly, in an appropriate trust environment, I might want to ship work units to remote machines in the form of raw ELF. Copying the bits to disk just to execute them is unneeded overhead.
- shawnz 7y agoBut a tmpfs (the usual way to do this) isn't persisted to a disk
- gnode 7y agoIt can be; swap and suspend-to-disk / hibernation can cause tmpfs to be written. I wonder if there's a way to execute from mlock'd or madvised memory. I can imagine this be particularly useful, if you wanted to prevent your injected program (malware? valuable IP?) from being written to disk or appearing in a core dump.
- shawnz 7y agoTrue, but those caveats apply to OP's technique too. What I am saying is that there is no specific behavior of a tmpfs which causes things in it to be necessarily persisted to disk like the parent was suggesting. It works just like any other anonymous memory does.
- gnode 7y agoIn the example given "We've managed to get access to a victim at 10.131.66.89", it's implied that nefariously injecting arbitrary binaries into a locked-down host is one use case. > otherwise the API would not have been introduced I don't think memfd_create was added specifically for the purpose of running binaries from memory; it's more generally useful in cases where you want a memory-backed file but don't want to rely on the presence of a memory-backed filesystem (tmpfs). Many programs create a file in a well-known tmpfs like /dev/shm then delete it, but memfd_create is a more sensible solution if you don't want to share memory via the filesystem. memfd_create eliminates concerns over naming and collisions.
- dimkr1 7y agoThere are other ways to do this that don't involve tmpfs or write() - see https://github.com/dimkr/papaw https://github.com/dimkr/papaw and https://github.com/dimkr/Mirai-Source-Code https://github.com/dimkr/Mirai-Source-Code
- adito 7y agoBut the readme says: The payload executable is extracted to a temporary file. When running as root, this is done by mounting a tmpfs file system and lazily unmounting it before the extraction.
- dimkr1 7y agoWhen not running as root, it doesn't use a tmpfs. Also, papaw replaces /proc/self/exe with an empty file. And it has some basic anti-debugging, like locking of the payload to RAM so it cannot be recovered by reading it from a swap partition.
- dg246 7y agoSimilar - https://blog.gdssecurity.com/labs/2017/9/5/linux-based-inter-process-code-injection-without-ptrace2.html https://blog.gdssecurity.com/labs/2017/9/5/linux-based-inter...
- joosters 7y agoA bit off-topic, but their perl code is buggy. fork() returns undef on error, not -1. And you can use '$FH->autoflush(1)' instead of the eye-watering 'select((select($FH), $|=1)[0])'
- jwilk 7y agoI was going to say that you need to use the IO::Handle module to be able to use the autoflush method, but apparently this no longer necessary with modern Perl. From https://metacpan.org/pod/perl5120delta#Other-potentially-incompatible-changes https://metacpan.org/pod/perl5120delta#Other-potentially-inc... : > Filehandles are now always blessed into IO::File. > The previous behaviour was to bless Filehandles into FileHandle (an empty proxy class) if it was loaded into memory and otherwise to bless them into IO::Handle.
- jwilk 7y agoOn a second thought, this is probably the right changelog entry: https://metacpan.org/pod/perl5140delta#Filehandle-method-calls-load-IO::File-on-demand https://metacpan.org/pod/perl5140delta#Filehandle-method-cal... When a method call on a filehandle would die because the method cannot be resolved and IO::File has not been loaded, Perl now loads IO::File via require and attempts method resolution again
- derefr 7y agoGiven that Linux now has both file descriptors, and process memory-page ranges, pointing to anonymous allocated memory regions, are they interchangeable? What I mean is: - to go from a memfd to anonymous memory, you can call mmap(2) on the fd - to go from anonymous memory to a memfd, you...?
- the8472 7y agoSemantically this should do the job: anonymous memory -> vmsplice(gift) -> pipe -> splice(move) -> memfd. Then remap the pages from memfd to the original address ranges so you're allowed to use them again. But I don't know whether this is supported on memfds. And of course it's not atomic.