Linux Capabilities for Privilege Escalation
You've seen SUID, where a binary runs with its owner's full privileges. Capabilities are the modern, fine-grained version: instead of giving a program all of root's power, the kernel splits root into ~40 separate capabilities and hands a binary only the specific ones it needs. Safer in theory, but the wrong capability on the wrong binary is still a straight line to root.
Fewer powers is safer — but the wrong single key (e.g. cap_setuid) is still a full root shell.
The idea
Take ping. It needs to open a raw network socket, a root-only action, but it doesn't need the rest of root's power. The old way was to make it SUID root (over-privileged). The capability way is to grant it only cap_net_raw:
/usr/bin/ping = cap_net_raw+ep
Now ping can open its socket and nothing more. That's the whole point of capabilities: least privilege, one power at a time. The letters after the +:
- e (Effective) , the capability is active the moment the binary runs.
- p (Permitted) , the binary is allowed to use it.
+ep is what you'll see on anything actually exploitable.
Enumerate them
One command lists every file carrying capabilities:
getcap -r / 2>/dev/null
Read the output looking for dangerous capabilities on general-purpose binaries (interpreters, shells, editors). cap_net_raw on ping is fine. cap_setuid on python is a root shell.
The capabilities that mean root
| Capability | What it grants | Why it's game over |
|---|---|---|
cap_setuid | set any user ID | become uid 0, straight to root |
cap_dac_read_search | bypass file read checks | read /etc/shadow, /root/*, any file |
cap_dac_override | bypass read/write/exec checks | read and write any file (edit /etc/passwd) |
cap_sys_admin | a huge grab-bag of admin powers | many paths to root |
Exploiting cap_setuid
If a program that can run other code has cap_setuid, tell it to become root and spawn a shell. With Python:
$ getcap -r / 2>/dev/null
/usr/bin/python3.11 = cap_setuid+ep
$ /usr/bin/python3.11 -c 'import os; os.setuid(0); os.system("/bin/bash")'
# id
uid=0(root) ...
GTFOBins lists the exact payload for each binary+capability pair (perl, ruby, node, gdb, and more), the same idea as SUID GTFOBins.
Exploiting cap_dac_read_search
You don't even need a shell, just read what you shouldn't:
# a binary with cap_dac_read_search can read root-only files
$ ./reader /etc/shadow # dump the hashes, then crack offline
The fix (what you report)
Grant capabilities sparingly and never on interpreters or shells. Audit with getcap -r /, remove any capability a binary doesn't strictly need (setcap -r <file>), and prefer capabilities over SUID only when the capability set is truly minimal. Report the exact file and capability.
Common questions
"How is this different from SUID? They both give root."
SUID hands over all of root at once. A capability hands over one specific power. cap_net_raw on ping is harmless because opening a socket isn't enough to own the box; cap_setuid on python is game over because "become any user" includes becoming root. The danger is the specific capability, not the fact that one exists.
"getcap -r / printed nothing. Does that mean I'm safe here?"
It means no files carry capabilities, so this vector is a dead end, move on to SUID, sudo, cron, or writable files. An empty result is a normal, common outcome; capabilities are a sometimes win, not an always.
"Which capabilities actually matter?"
Memorise three: cap_setuid (become root), cap_dac_read_search (read any file, e.g. /etc/shadow), cap_dac_override (read and write any file, e.g. /etc/passwd). cap_sys_admin is a grab-bag with many paths too. The rest are rarely the direct route.
"What do +ep, +ei, +p mean at the end?"
They're the capability's states. e = Effective (active as soon as it runs), p = Permitted (allowed to use). +ep is the exploitable combo; a capability that's only Permitted or Inheritable (+i) may need extra steps to activate.
Why this matters
Capabilities look obscure, so they're often overlooked by both defenders and beginners, which is exactly why they're a reliable privesc. getcap -r / takes one second and, on a surprising number of boxes, prints your way to root.