And 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.
Organizing 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.
It 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.
True, 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.
In 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.
Runc used by Docker actually does this. The reason is that you don't want /proc/self/exe to refer to any host path when spawning the container process (see https://blog.dragonsector.pl/2019/02/cve-2019-5736-escape-fr...). The way to fix this is to reexecute the runc binary through a memfd so that /proc/self/exe is that memfd.
It lets you execute files even with no writeable filesystem , which could be useful exploiting a locked down system. It is also an argument that locking down programs with no writeable fs is not sufficient for security.
The memfd api has lots of other uses, the fact you can exec is just a byproduct.
> It is also an argument that locking down programs with no writeable fs is not sufficient for security.
It is in fact silly since write and execute are orthogonal. You can mount a filesystem as noexec. Although memfd does put a bullet into W^X aspirations.
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.
When 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.
I 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.
> 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.
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
Given 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
Semantically 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.
23 comments
[ 2.7 ms ] story [ 53.7 ms ] threadIt doesn't work worth shbang-based scripts, though; that just returns ENOENT. Makes sense, I suppose.
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.
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.
> 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.
The memfd api has lots of other uses, the fact you can exec is just a byproduct.
It is in fact silly since write and execute are orthogonal. You can mount a filesystem as noexec. Although memfd does put a bullet into W^X aspirations.
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.
And you can use '$FH->autoflush(1)' instead of the eye-watering 'select((select($FH), $|=1)[0])'
From 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.
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
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...?