skip to content

How can a Linux process execute a binary that has no name in any filesystem?

level: middleimportance: nice to knowfreq 32%

answer

  1. a file needs no name
  2. descriptor without a directory entry
  3. memory-backed, not on a mount
  4. noexec applies to mounts only
  5. deleted now is not never existed

basics

~20 s

The kernel can create an anonymous, memory-backed file that has a descriptor but no directory entry. Bytes are written into it and executed from that descriptor, so the running image has no path and dies with the process.

solid answer

~50 s

Linux lets a process create an anonymous file that lives in memory and is referred to only by a file descriptor - `memfd_create` is the usual route. The payload is written into that descriptor and then executed directly from it, either via `execveat` with an empty path or through the descriptor's entry under `/proc`. Because the object was never linked into a directory, it has no path anyone can open by name, it is not on a mounted filesystem, and mount options such as `noexec` on writable directories are irrelevant to it. The running process's image path resolves to an anonymous object marked deleted. Note the limits: the operator must deliver the whole payload every time, since nothing durable holds it, and the bytes vanish when the last descriptor closes - which for a single-process payload means at exit or reboot. That is a deliberate trade, not an oversight, for a crew that can simply re-exploit.

code

text · 9 lines
text
# process A - written to a writable directory, executed, then unlinked
/proc/8811/exe  -> /var/tmp/.cache1 (deleted)
/proc/8811/cwd  -> /srv/app

# process B - anonymous memory-backed file, no directory entry ever existed
/proc/9024/exe  -> /memfd:job (deleted)
/proc/9024/cwd  -> /srv/app
...
# '(deleted)' means: no directory entry NOW. It does not mean: never had one.

go deeper

for a junior

Know that on Linux a file is an inode plus data, and a name is just a directory entry pointing at it. That single fact is what makes a nameless executable possible.

for a middle

Explain the sequence: an anonymous memory-backed file is created, bytes are written to the descriptor, and the descriptor itself is executed. Be able to say why mount-level execution restrictions do not apply.

for a senior

Distinguish the two ways an image ends up nameless and state what each leaves behind. Then price the technique honestly: no durability, full re-delivery each time, and memory attributed to an odd process.

for a principal

Frame it as a control-class question for the estate: hardening that acts on mounted filesystems has a boundary, and knowing where that boundary lies decides whether the money goes into execution restrictions or into closing the entry path.

## The mechanism On Linux, a file does not need a name. A file is an inode plus data; a *name* is a directory entry pointing at that inode. The kernel offers a way to create an inode with data and **no directory entry at all**: an anonymous, memory-backed file, obtained through `memfd_create`, which returns a file descriptor and nothing else. Bytes written to that descriptor live in memory (charged to the process, like any anonymous memory), not on any block device. That descriptor can then be executed. `execveat` with an empty path and the flag that means `use this descriptor as the program` starts it directly; equivalently, the descriptor is visible to the process through `/proc` and can be executed by that pseudo-path. Either way the kernel loads the image from an object that no directory ever pointed to. ## What the running process looks like The image path of the running process resolves to an anonymous object with a `(deleted)` marker. That is the same marker you get from a completely different and much older trick - write the payload to a normal directory, execute it, then unlink it while it is still running. Both end up nameless; only one of them ever touched a filesystem. ```text # process A - payload written to a writable directory, executed, then unlinked /proc/8811/exe -> /var/tmp/.cache1 (deleted) # process B - payload never had a directory entry at any point /proc/9024/exe -> /memfd:job (deleted) ``` The distinction is worth internalising because `(deleted)` alone establishes only that the running image has no directory entry now - not that it never had one. ## Why an operator bothers **Mount options stop mattering.** A common hardening step on an application server is to make every directory the service account can write to non-executable. That control acts on a mounted filesystem. An anonymous memory object is not on one, so the restriction does not reach it. This is a large part of why the technique exists at all. **Nothing is left as a program.** There is no path to open, no image on disk to hash or submit anywhere, and no file whose creation or modification time records the moment the operator arrived. **It fits the position.** Code already running as the application's service account, with no privilege beyond it, can do all of this: creating an anonymous file and executing it needs no elevation. ## What it costs the operator **Nothing survives the process.** The bytes exist only while a descriptor is open. Exit, restart or reboot and they are gone - there is no durable copy anywhere, because the whole point was not to make one. **The payload must be delivered every time.** Each execution requires the full bytes to arrive again, which means either a fetch over the network or a re-run of the exploit that got the operator here. **It is memory, and memory is accounted.** A large payload held this way shows up as memory attributed to a process that has no reason to hold it. **No stealth about privilege.** The technique conceals the image, not the process. The process exists, runs as a known account, and does whatever it does under that account's limits. ## The economics that make it a good trade anyway For a commodity crew mass-exploiting one flaw in an internet-facing application, all of those costs are cheap and the benefit is real. Re-entry costs one request. Persistence costs work and plants something durable that an owner can find. So the correct read of a memory-only, nothing-planted payload is frequently not *the operator failed to persist* but *the operator chose not to*, because the way in is still open and is cheaper than the alternative. ## Not to be confused with This is about the operator's **own** process running a nameless image. Getting code into somebody else's live process to inherit its identity and its normal destinations is a different technique with a different cost profile. And a payload carried as data for an installed interpreter is a third shape: there the executable never exists at all, not even anonymously - the runtime is the executable.

  • Why does making every writable directory non-executable fail to stop this?
    Because that restriction is a property of a mounted filesystem, applied when the kernel executes a file on that mount. An anonymous memory-backed object is not on any mount, so there is no mount option in the path of the decision. The hardening still has value against the write-then-execute variant; it just does not reach this one.
  • What does an image path marked '(deleted)' establish on its own?
    Only that the running program has no directory entry pointing at it right now. That is equally true of a payload written to disk and unlinked afterwards and of one that never had a name. Concluding 'it never touched disk' from the marker alone is an overreach, and the two cases differ in what they left behind.
  • If nothing durable is planted, what should the conclusion be about the operator?
    Usually that they preferred re-entry to residence. A crew exploiting one internet-facing flaw at scale gets back in for the cost of a request, so building persistence buys them nothing and plants something durable. The follow-up question is whether the flaw is closed, not where the body was hidden.

saying these in an interview costs you the question

  • Believes execution always requires a path on a filesystem
  • Thinks noexec on writable directories blocks this
  • Reads '(deleted)' as proof nothing was ever written
  • Assumes the technique needs root privilege
  • Calls a disposable payload a failed persistence attempt

context