Linux Capabilities Revisited
A DFIR.ch technical walkthrough of Linux POSIX capabilities: how to weaponize cap_setuid on a binary like /usr/bin/python3.12 with setcap to create a stealthy root-spawning backdoor without touching SUID bits, then how defenders can hunt for these via getcap -r, /proc status Cap* fields, LinPEAS, and an Elastic detection rule that flags setcap process execution. It also explains where capabilities live on disk (the security.capability extended attribute on the inode) and how to inspect/remove them with getfattr and setcap -r.
What it is
A DFIR.ch walkthrough of using Linux POSIX capabilities — specifically cap_setuid — as a SUID-free privilege escalation backdoor, plus the enumeration commands to hunt it. The concrete artifact is a cap_setuid=ep capability set on the Python binary, which lets any user spawn a UID 0 shell without a SUID bit or a modified binary.
How it works
setcap writes capability flags into the file's inode extended attributes. cap_setuid permits arbitrary UID manipulation, including setting UID to 0. The +ep suffix marks the capability Effective (activated) and Permitted (allowed). Once set, an unprivileged user calls Python's os.setuid(0) and gets root.
# setcap cap_setuid+ep /usr/bin/python3.12
$ /usr/bin/python3 -c 'import os;os.setuid(0);os.system("/bin/bash")'What to do
Enumerate every binary with capabilities — this is the parallel to SUID hunting, and ls will not show these flags:
# getcap -r / 2>/dev/nullInspect a running process's capabilities:
# cat /proc/<PID>/status | grep Cap
# getpcaps <PID>
# capsh --decode=0000000000000080The status fields decode as: CapInh inherited, CapPrm permitted, CapEff effective, CapBnd bounding set, CapAmb ambient.
Remove a planted capability:
# setcap -r /usr/bin/python3.12Because the flags live in inode extended attributes (same location as ACLs and setuid/setgid bits), dump all extended attributes to audit a file directly:
# getfattr -d -m -The relevant extended attribute name is security.capability. Check /proc/sys/kernel/cap_last_cap for the highest capability number the kernel knows.
Detection
The Elastic rule "Process Capability Set via setcap Utility" fires on setcap execution, excluding a fixed set of legitimate parents:
process where host.os.type == "linux" and event.type == "start" and event.action in ("exec", "exec_event", "start") and
process.name == "setcap" and not (
process.parent.executable == null or
process.parent.executable : ("/var/lib/dpkg/*", "/var/lib/docker/*", "/tmp/newroot/*", "/var/tmp/newroot/*") or
process.parent.name in ("jem", "vzctl")
)Note this detects the setting of the capability, not later abuse of an already-planted one — an attacker who set the flag before logging began, or via a whitelisted parent path, evades it. Combine with periodic getcap -r / sweeps.
Organisations named here
- cap_setuid
- security.capability
Technologies named here
- capsh
- Linux
- Python
- setcap
- getcap
- getpcaps
- getfattr
- LinPEAS
- Elastic
- Linux kernel
- Elasticsearch
Techniques named here
- Backdooring via file capabilities
- Enumeration of capabilities
Related in Offensive Security