# tl; dr I was interested in exploring other options for simple in-memory ELF loading aside from `memfd_create`. Running a file from `tmpfs` is *technically correct* while not being useful at all. But what if we could create our own `tmpfs`, that could only be used by the ELF loader and nothing else? This is how it'll look like (code is on https://github.com/zimnyaa/fuse-memload): ``` root@kali:~/fuse/fuse-example# make make: building and encoding the example binary make: compiling the driver make: creating the mountpoint at ./mountp make: running the FS main: decoding with key ffffffffmain: filemountpath: ./mountp/memfile main: fuse args: fuse ./mountp main: starting polling polling... main: file at [./mountp/memfile] exists now child 3452848: i am alive! child 0: i am alive! child 0: and I can run subprocesses! child 3452848: and I can run subprocesses! ^Cmakefile:16: recipe for target 'run' failed make: *** [run] Interrupt root@kali:~/fuse/fuse-example# make clean make: unmounting make: killin the driver make: removing artifacts root@kali:~/fuse/fuse-example# ``` And the file is inaccessible for any other process (explained below): ![[Pasted image 20230404161824.png]] > **note:** I've tested it, and I was not able to arrive at the idea by just asking the language model. I guess that should account for something. Or, quite possibly, I'm bad at prompts. # `libfuse` (I will never create a kernel module willingly) For creating a filesystem driver, `libfuse` is an obvious solution. In essence, the `fuse` kernel module allows a userland process to expose callbacks for filesystem functions like `open` or `read` in order to mount an arbitrary filesystem. This makes creation of an in-memory filesystem very simple, especially if I want to only expose a single file. In fact, someone else has [done it already](https://github.com/fntlnz/fuse-example), so huge thanks to Lorenzo Fontana for this 8 year old example. I will use it as a skeleton for the implementation. # counting `opens` I have implemented a primitive `open` counter: ```c char open_count; static int open_callback(const char *path, struct fuse_file_info *fi) { open_count++; return 0; } ``` And my initial idea was to check whether it met a certain threshold: ```c // in read_callback const char open_threshold = 1; if (open_count > open_threshold) return -ENOENT; ``` Unsurprisingly, this crashed the child process with a `bus error`. This is how I learned (I am not very good with Linux internals) that the process reopens its executable file sometimes. However, I have also realised that it does not open itself at the beginning of the file, so I can rework my condition to look like this: ```c // in read_callback if (offset == 0 && open_count > open_threshold) return -ENOENT; ``` And it actually works! # making it happen Because the filesystem is dynamic, we can decode or decrypt our ELF however we want (`xor 0xff` for demonstration purposes). The whole process is pretty simple: ```j decode ELF -> fork -> run FUSE userland driver (fork == 0) \ \-> poll for the file (fork != 0) -> execve(file) ``` # process stability One would expect this process to crash almost immediately, but it can sucessfully fork and run subprocesses (which should be enough for an implant): ```c // executable-example.c #include <stdio.h> #include <stdlib.h> #include <unistd.h> void main(void) { char cmd[255] = {0}; int me = fork(); sprintf(cmd, "echo child %d: and I can run subprocesses!", me); while (1) { printf("child %d: i am alive!\n", me); system(cmd); sleep(2); } } ``` # IoCs and caveats - obviosuly, there is a `fuse` filesystem mounted - the file is still visible in `ls` (change `readdir` to change that) - there will be a companion process that acts as the FUSE driver - EDRs can scan `/proc/<PID>/mem` and the execution method will not matter # references https://github.com/fntlnz/fuse-example