20

Suppose my non-root 32-bit app runs on a 64-bit system, all filesystems of which are mounted as read-only. The app creates an image of a 64-bit ELF in memory. But due to read-only filesystems it can't dump this image to a file to do an execve on. Is there still a supported way to launch a process from this image?

Note: the main problem here is to switch from 32-bit mode to 64-bit, not doing any potentially unreliable hacks. If this is solved, then the whole issue becomes trivial — just make a custom loader.

3
  • If you have a tmpfs, you could write the image there and execute it. tmpfs is backed entirely by memory. Not sure this fits your requirements, though.
    – alienth
    Sep 18, 2015 at 6:40
  • @alienth to mount a tmpfs I'd need root privileges.
    – Ruslan
    Sep 18, 2015 at 10:21
  • 1
    Ruslan: @alienth's suggestion is to keep a tmpfs filesystem permanently mounted. This doesn't let any of the read-only memory pages in your 64bit process be backed by the contents of the disk file holding the 32bit executable, though. Instead, everything it needs has to be copied. (Unless you have the 64bit program open and mmap after it starts.) Also, you'd need to make sure you clean up the tmpfs, to avoid upward-creeping memory usage. Sep 18, 2015 at 17:39

3 Answers 3

28

Yes, via memfd_create and fexecve:

int fd = memfd_create("foo", MFD_CLOEXEC);
// write your image to fd however you want
fexecve(fd, argv, envp);
2
  • 5
    Great. I've checked, this indeed works (see this example code (the child simply spins increasing rax)). But a couple of comments: memfd_create first appears in Linux 3.17. Include <sys/mman.h>, having defined _GNU_SOURCE (man page appears wrong about the include and doesn't mention the need for the define).
    – Ruslan
    Sep 3, 2018 at 21:01
  • 3
    @Ruslan Good find. I just sent in a patch to add the _GNU_SOURCE requirement to the man page. (The header name is already fixed in git; the website just hasn't updated yet.) Sep 3, 2018 at 22:38
2

You're looking for something like "userland exec". Implementation here. Basically, this involves loading some position-independent code that has no external references into memory, and marking it executable. This position independent code removes the previously-running executable, and reloads. Sounds like you might have to modify the userland exec I wrote at least a little.

3
  • 1
    IDK if you can change from compat (32bit) mode to long (64bit) mode other than with execve. It's going to require a kernel call of some sort. personality(2) sounds like it might be the right thing, but it's for stuff like changing signal numbers to emulate a BSD or other non-Linux system call ABI, I think. man7.org/linux/man-pages/man2/personality.2.html Sep 18, 2015 at 17:44
  • 1
    The only way to switch from 32bit to 64bit mode I've come up with is quite hacky. One can't even guarantee that it won't break in a newer kernel. Thus the implementation you point to doesn't seem workable in my use case. And, even if I do use the above mentioned hack, the process still looks like 32 bit to the kernel: e.g. ptrace'ing it with GETREGSET with NT_PRSTATUS gives 32-bit regset.
    – Ruslan
    Sep 18, 2015 at 18:42
  • As far as I can tell this doesn't close CLOEXEC fds Feb 21, 2019 at 20:18
0

The comment by user732 and the link is outdated. But the gist is right; you need a "userland exec" implementation of which there are several. One that is useful in scenario's where you can't write anywhere but can execute Python scripts can be found at https://github.com/anvilsecure/ulexecve/. I wrote this one and you might want some more context via the overview blogpost introducing it to the world: https://www.anvilsecure.com/blog/userland-execution-of-binaries-directly-from-python.html

It should be rather trivial to change the code of ulexecve above such that it switches as well from 32-bit to 64-bit mode or vice versa. That's an interesting use case I didn't think about nor implement. But the approach sketched by @Ruslan should work.

Rapid7's Mettle also has a utility named noexec that basically does the same thing: https://github.com/rapid7/mettle. This is a lower-level utility written in C so you would end up with another ELF binary that would then allow you to do a userland execution.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .