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 whypasswdcan change/etc/shadowwithout 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 /tmprws→ SUID is set (thesreplaces the owner’sx)rwton/tmp→ sticky bit is set- if you see an uppercase
SorT, 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=epcap_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 capabilitiesKeeping 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)