06 · Advanced Permissions: SUID, SGID, Sticky Bit & ACLs¶
The standard rwx × owner/group/other model (Lesson 5) can't express "let any user run this program with the
file owner's privileges" or "let everyone create files here, but only the creator can delete their own" — both are
common, real requirements, and both are handled by three special bits plus an extension mechanism that goes beyond
the classic nine permission bits entirely.
The three special bits¶
SUID (set-user-ID, shown as s in the owner's execute position) makes a program run with the file owner's
UID, not the invoking user's UID, for as long as that process runs. SGID (set-group-ID, same idea in the group
execute position) does the equivalent for the group; on a directory specifically, SGID means something extra:
new files created inside inherit the directory's group rather than the creating user's primary group — the
standard way to make a shared team directory where everyone's files end up in the same group automatically. The
sticky bit (shown as t in the other execute position) means something different again, and only matters on
directories: it restricts deletion — inside a sticky directory, you can only delete or rename files you own,
regardless of write permission on the directory itself.
Worked example: passwd, a shared directory, and /tmp¶
$ ls -l /usr/bin/passwd
-rwsr-xr-x 1 root root 142552 Feb 3 2026 /usr/bin/passwd
$ ls -ld /tmp
drwxrwxrwt 12 root root 240 Oct 22 23:30 /tmp
$ mkdir team-shared && chmod 2775 team-shared
$ sudo chgrp testeng team-shared
$ ls -ld team-shared
drwxrwsr-x 2 vinodkumarsigilipelli testeng 40 Oct 22 23:32 team-shared
$ id
uid=501(vinodkumarsigilipelli) gid=1000(vinodkumarsigilipelli) groups=1000(vinodkumarsigilipelli),999(systemd-journal)
$ touch team-shared/report2.txt
$ ls -l team-shared/report2.txt
-rw-rw-r-- 1 vinodkumarsigilipelli testeng 0 Oct 22 23:32 team-shared/report2.txt
(Real output from this course's lab VM.) passwd is owned by root and carries the SUID bit (the s where you'd
expect x) — which is why an unprivileged user running passwd can update /etc/shadow, a file only root can
normally write. Without SUID, every user would need root access just to change their own password, which defeats
the point of having unprivileged accounts at all; SUID lets the program temporarily act with elevated privilege
while still being invoked by a normal user, and the program itself is responsible for only doing the one narrow,
safe thing it was written to do (update the calling user's own password entry, nothing else). /tmp's permissions
(rwxrwxrwt, that trailing t) show the sticky bit in the wild: everyone can create files in /tmp, but the
sticky bit stops one user from deleting another user's temp files even though the directory itself is
world-writable. chmod 2775 on team-shared sets SGID (the leading 2); id confirms this shell's primary group
is still vinodkumarsigilipelli (gid 1000) — yet the file it creates, report2.txt, comes out owned by group
testeng, the directory's group, not the creating process's primary group. That's SGID-on-a-directory working
exactly as specified: inheritance happens regardless of what group the creating process would otherwise have used.
How It Actually Works¶
The kernel implements SUID/SGID by checking, at execve() time (the syscall that actually replaces a process's
code with a new program), whether the target file has the SUID or SGID bit set. If so, instead of the new process
keeping the calling process's real UID/GID as its effective UID/GID (the normal case — effective IDs are what
permission checks actually use), the kernel sets the new process's effective UID/GID to the file's owner/group.
The process's real UID (who actually invoked it) is preserved separately, which is how passwd and similar tools
can tell "who is calling me" from "what privilege do I currently have" and behave accordingly rather than blindly
trusting the elevated privilege for everything.
This mechanism is also exactly why SUID is treated as a security-sensitive feature rather than a convenience: any
bug in a SUID-root binary is a potential privilege escalation path, because the bug now executes with root's
effective UID regardless of who triggered it. This is why the Linux kernel, by default, refuses to honor the SUID
bit on shell scripts (even though you can technically chmod u+s one) — a script's first line only names an
interpreter, and there's a well-known race window between the kernel opening the script and the interpreter
re-opening it, during which the file on disk could be swapped out from under the privilege check; compiled binaries
don't have that two-step open. It's also why security audits specifically grep filesystems for unexpected SUID
binaries (find / -perm -4000 -type f) — a SUID binary that shouldn't be there, or a legitimate one with a known
vulnerable version, is one of the most direct routes from "a foothold as a normal user" to "root."
POSIX ACLs (setfacl/getfacl) exist for everything the owner/group/other model genuinely can't express at
all: granting one specific additional user (not a whole group) read access to a file, for instance, without
changing its owner or creating a new group just for that one grant. getfacl on a file with ACLs set shows the
extra entries, and ls -l signals their presence with a trailing + after the permission string — a detail
that's easy to miss and a common source of "why can this person read a file whose ls -l bits say they shouldn't
be able to" confusion during audits.
Common mistakes¶
- Setting SUID on a shell script and being confused when it "doesn't work." As explained above, the kernel
deliberately ignores SUID on interpreted scripts on most systems; if you need SUID behavior for a script's logic,
the standard fix is a small compiled wrapper binary that is itself SUID and that
execs the script non-SUID. - Confusing the sticky bit with SGID. They occupy similar-looking positions in
ls -loutput (tvssin the "other" vs "group" execute slot) but do unrelated things — sticky restricts deletion, SGID affects group ownership inheritance (on directories) or effective GID (on executables). - Forgetting that
chmod -Ron a directory tree with SGID set recalculates the special bits per-file, often stripping them from regular files (which don't need SGID at all) while potentially leaving directories correct — always re-check withfind dir -type d -perm -2000after a bulkchmodif SGID inheritance matters.
Exercise¶
Find every SUID and SGID binary on a Linux machine you control with find / -perm -4000 -o -perm -2000 -type f
2>/dev/null, pick three, and for each one explain in one sentence why it plausibly needs elevated privilege (or
flag it as suspicious if you can't justify it). Then create a shared-group directory with SGID set, have two
different users (or simulate with sudo -u) create files in it, and confirm both files end up in the same group
despite the users having different primary groups.