SUID, SGID & File Capabilities

3 minuti

Beyond the basic rwx permissions seen in Permissions & Ownership, Linux has three special permission bits: SUID, SGID and the sticky bit. They change who a program runs as, which makes them the first place an attacker looks when trying to escalate privileges.

The three special bits

  • SUID (4000): when executed, the program runs with the privileges of the file owner (usually root). That’s why passwd can change /etc/shadow without giving you write access to it.
  • SGID (2000): the program runs with the privileges of the file group. On directories it behaves differently: every new file created inside inherits the directory’s group — useful for shared project folders.
  • Sticky bit (1000): on directories (like /tmp), only a file’s owner can delete or rename it, even if the directory is world-writable.

Reading them in ls -l

The special bit appears where the x of owner (SUID), group (SGID) or others (sticky) would be:

ls -l /usr/bin/passwd /tmp
 
# Output
-rwsr-xr-x. 1 root root 68208 Jan  1  2024 /usr/bin/passwd
drwxrwxrwt. 15 root root 4096 Nov  6 10:00 /tmp
  • rws → SUID is set (the s replaces the owner’s x)
  • rwt on /tmp → sticky bit is set
  • if you see an uppercase S or T, the bit is set but the underlying execute permission is missing — usually a red flag

In octal mode they are a fourth digit in front of the usual three:

chmod 4755 script.sh   # SUID + rwxr-xr-x
chmod 2755 shared/     # SGID on a directory
chmod 1777 shared-tmp/ # sticky + world-writable
# symbolic form works too:
chmod u+s script.sh
chmod g+s shared/
chmod +t shared-tmp/

Why they matter: privilege escalation

A SUID binary owned by root runs as root. If it can be tricked into launching a shell or reading arbitrary files (through a feature, a plugin, an environment variable…), the attacker gets root too. The community catalog of such tricks for common binaries is GTFOBins — worth browsing to understand the patterns, and to audit the binaries on your own machines.

File capabilities: the modern alternative

Instead of the all-or-nothing “run as root”, the kernel supports fine-grained capabilities. ping needs raw network sockets, not full root:

setcap cap_net_raw+ep /usr/bin/ping
getcap /usr/bin/ping
 
# Output
/usr/bin/ping cap_net_raw=ep
  • cap_net_bind_service → bind ports below 1024 (web servers, no root needed)
  • cap_sys_time → change the system clock
  • every capability dropped is one less thing an attacker can abuse

To audit both worlds on a system:

find / -perm -4000 -type f 2>/dev/null   # SUID binaries
find / -perm -2000 -type f 2>/dev/null   # SGID binaries
getcap -r / 2>/dev/null                  # files with capabilities

Keeping things safe

  • remove SUID from anything that doesn’t strictly need it: chmod u-s
  • prefer narrow file capabilities over SUID root
  • audit the lists above after every package install (see Logs & Auditing to monitor changes)
  • a new SUID binary appearing out of nowhere is one of the classic post-compromise signals (see Processes & Persistence)