Users & Permissions

Setuid & Setgid

How setuid, setgid, and the sticky bit work, and why setuid binaries are prime escalation targets.

Some tasks genuinely require a normal user to run code as someone more privileged: passwd must write the root-owned shadow file, ping once needed raw sockets. The setuid bit makes this possible by running a program as its file owner rather than its caller. That power is also the single most reliable local privilege escalation surface on Linux, because a setuid-root binary with any flaw hands root to whoever exploits it. This page explains the mechanism, teaches you to enumerate and audit these files, and covers the sticky bit that protects shared directories. Snapshot the lab first.

What Happens When A Setuid Binary Runs

Normally a process runs with the UID of whoever launched it. When a binary has the setuid bit set, the kernel instead sets the process’s effective UID to the file’s owner for the duration of that program. If the owner is root, the program runs with root’s power no matter who started it. Setgid does the same for the group.

You see the bit as an s in the owner’s execute position from ls -l:

ls -l /usr/bin/passwd

Expected output:

-rwsr-xr-x 1 root root 59976 Apr  8  2024 /usr/bin/passwd

The s where the owner’s x would be means setuid; owned by root, so passwd runs as root even when you launch it. In octal, setuid is a leading 4 (setgid 2, sticky 1), so chmod 4755 sets setuid on a 755 file. Demonstrate the identity change with a tiny program in the lab:

cat > /tmp/whoisit.c <<'EOF'
#include <stdio.h>
#include <unistd.h>
int main(){ printf("real uid=%d, effective uid=%d\n", getuid(), geteuid()); return 0; }
EOF
gcc -o /tmp/whoisit /tmp/whoisit.c
sudo chown root:root /tmp/whoisit
sudo chmod 4755 /tmp/whoisit
/tmp/whoisit

Expected output:

real uid=1000, effective uid=0

You launched it as UID 1000, but the effective UID is 0: inside that program you are root. If such a program lets you influence what it executes (a shell escape, an injected library, a path it trusts), that root context becomes yours. That is why every setuid-root binary is a target worth auditing.

Finding And Auditing Setuid/Setgid Files

Because these files are escalation surface, you enumerate them and confirm every one belongs. find locates them with the -perm mode flags: -4000 matches setuid, -2000 setgid.

find / -perm -4000 -type f 2>/dev/null

Expected output:

/usr/bin/passwd
/usr/bin/sudo
/usr/bin/su
/usr/bin/chsh
/usr/bin/chfn
/usr/bin/newgrp
/usr/bin/gpasswd
/usr/bin/mount
/usr/bin/umount
/usr/lib/openssh/ssh-keysign

That list is a fresh Ubuntu baseline, and every entry has a reason to be setuid. The audit skill is spotting additions. Compare a suspect host’s list against this known-good set; anything extra, especially in /tmp, /home, or a custom path, is suspicious. Our lab now has one such stray:

find / -perm -4000 -type f 2>/dev/null | grep -vE '^/usr/(bin|lib)'

Expected output:

/tmp/whoisit

/tmp/whoisit is exactly the kind of anomaly an attacker leaves behind: a setuid-root binary that reopens root access later. Remove the bit (sudo chmod u-s /tmp/whoisit) or the file entirely. On a real host, record the baseline setuid list and alert on any change. Setgid on a binary works the same way for group identity; setgid on a directory does something else entirely, which the next section covers.

The Sticky Bit On Shared Directories

/tmp is world-writable so any user can create files there, which raises an obvious problem: what stops one user from deleting or replacing another user’s files? The sticky bit. On a directory, it restricts deletion and renaming to the file’s owner (and root), even though everyone can write to the directory. You see it as a t in the other-execute position.

ls -ld /tmp

Expected output:

drwxrwxrwt 10 root root 4096 Jul  6 09:40 /tmp

The trailing t in rwxrwxrwt is the sticky bit. Prove it protects files across users:

sudo -u alice touch /tmp/alicefile
sudo -u nobody rm /tmp/alicefile

Expected output:

rm: cannot remove '/tmp/alicefile': Operation not permitted

nobody can write in /tmp but cannot remove alice’s file, because the sticky bit ties deletion to ownership. Set it yourself on any shared directory with chmod +t dir or octal 1777. Without it, a world-writable directory is a place where any user can clobber any other user’s files, a classic source of tampering and race-condition attacks.

Setuid is one way privilege is delegated; compare it with the policy-driven delegation in Sudo and the finer-grained alternative of file capabilities in ACLs & Attributes, which often let you avoid setuid-root entirely. The permission bits themselves are covered in File Permissions.

Practical Guidance

  1. Treat every setuid-root binary as attack surface; the fewer there are, the smaller your local escalation risk.
  2. Record a baseline of setuid/setgid files with find / -perm -4000 -o -perm -2000 and alert on any addition.
  3. Investigate any setuid file outside /usr/bin and /usr/lib immediately; strays in /tmp or /home are near-certain compromise markers.
  4. Prefer file capabilities over setuid-root when a program needs just one root power; grant the narrow capability instead.
  5. Ensure every world-writable shared directory has the sticky bit (1777) so users cannot delete each other’s files.
  6. Never make a shell, editor, or interpreter setuid-root; it is an instant root shell for anyone who runs it.