No File, No Path, Still Running: Inside Linux Fileless Execution

mashally1 pts0 comments

No File, No Path, Still Running: Inside Linux Fileless Execution | MrN1gativ3Table of Contents

Where It All Started<br>Honestly, I&rsquo;m not even sure how I ended up here.<br>It started with process injection on Linux something I was poking at in my spare time between work. One thing led to another, and at some point I searched online and stumbled across something called Linux fileless malware. I&rsquo;d heard of fileless attacks on Windows, but Linux? That caught my attention. I thought I knew what it meant. Run something without dropping a file. Simple.<br>Except it wasn&rsquo;t.<br>The more I read, the more questions I had. Where do the bytes actually live if there&rsquo;s no file? What does the kernel think is happening? Which syscall actually triggers execution and why does it matter which one? I&rsquo;d answer one question and three more would show up. That&rsquo;s usually the sign that something is worth digging into properly.<br>The articles I found were good but incomplete most covered one angle, one technique, one tool. Nobody had written the thing I actually wanted to read: a complete walkthrough of how this works at the kernel level, from the first syscall to a fully running in-memory process, with explanations for what&rsquo;s happening at each step and why.<br>So I wrote it myself.<br>This series is the result of a few months of going through kernel source code, running experiments, and building a mental model that finally felt solid. It&rsquo;s not a malware analysis and it&rsquo;s not a threat intel report. It&rsquo;s just me trying to understand something properly and documenting what I found the mechanics, the internals, and why this technique is considerably more interesting than the one-line definition suggests.<br>Wait, What Is &ldquo;Fileless&rdquo; Actually?<br>The term fileless is a little misleading. It doesn&rsquo;t mean there&rsquo;s no file involved at all it means no persistent ELF binary is ever written to a block-device-backed filesystem before it runs. To understand why that matters, think about how a normal binary executes: you compile it, it sits on disk as a file, the kernel opens that file, reads the ELF headers, maps the segments into memory, and runs it. The file on disk is the starting point for everything. Fileless execution cuts that starting point out entirely. The payload never touches the HDD or SSD instead it lives in RAM from the moment it exists. The kernel is handed a reference to that chunk of memory as if it were a regular file, and it launches the process straight from there. From the kernel&rsquo;s perspective the execution looks completely normal it&rsquo;s reading an ELF, setting up memory regions, mapping segments. The only difference is that the &ldquo;file&rdquo; it&rsquo;s reading from is backed by RAM, not a block device. And that one difference is exactly why legacy disk-scanning tools never see it there&rsquo;s nothing on disk to scan.

The Three System Calls Behind Fileless Execution<br>The whole trick comes down to three syscalls. First, memfd_create() creates an anonymous file that lives purely in RAM no path, no disk backing, just a file descriptor pointing at a chunk of memory. The MFD_CLOEXEC flag is worth understanding here: it closes the file descriptor the moment execveat() fires, but that doesn&rsquo;t kill the payload. The fd and the inode are two completely different things when execveat() fires, load_elf_binary() maps the PT_LOAD segments from that inode into the new process&rsquo;s virtual address space, and those VM mappings hold their own reference to the inode. So the process keeps running fine with the fd gone. The inode only gets freed once the process exits and those mappings get torn down no disk trace ever, no memory trace after exit. Second, you fill the fd write() if the payload is already in a buffer, sendfile() if it&rsquo;s coming over a socket, or mmap()+memcpy() if it&rsquo;s already mapped somewhere in memory. Doesn&rsquo;t matter which one you pick, the bytes land in RAM-backed page cache either way block device never comes into the picture. Third, execveat(fd, "", ..., AT_EMPTY_PATH) fires it the empty pathname combined with AT_EMPTY_PATH tells the kernel to skip pathname resolution entirely and just operate on the fd directly. Three syscalls, three jobs: create the container, fill it, run it disk never enters the picture at any step.<br>// fileless execution three syscalls, zero disk

// ── 1. memfd_create() ─────────────────────────────────────────────────<br>fd = memfd_create("payload", MFD_CLOEXEC)

// ── 2. fill the fd with the ELF payload ──────────────────────────────<br>write(fd, elf_buf, size) → payload already in a buffer<br>sendfile(fd, socket_fd, size) → payload arriving on a socket<br>mmap(fd) + memcpy(elf_buf) + munmap → payload already mapped

// ── 3. execveat() ─────────────────────────────────────────────────────<br>execveat(fd, "", argv, envp, AT_EMPTY_PATH)

From Userspace to Kernel: Breaking Down Each System Call<br>memfd_create(name, flags)...

rsquo file fileless kernel disk payload

Related Articles