# 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