Quick Start: Spot and Set Special Permissions in 5 Minutes
You’re browsing a directory with ls -l and notice something odd — an s where you’d normally see an x, or a t hanging at the end of a permission string. These are Linux’s special permission bits: SUID, SGID, and Sticky Bit. Most sysadmins recognize the symbols. Far fewer understand what’s actually happening under the hood when those bits fire.
Here’s how to find them immediately:
# SUID — notice the 's' in the owner execute position
ls -l /usr/bin/passwd
# -rwsr-xr-x 1 root root 68208 Apr 16 2024 /usr/bin/passwd
# Find all SUID files on the system
find / -perm -4000 -type f 2>/dev/null
# Find all SGID files
find / -perm -2000 -type f 2>/dev/null
# Find directories with Sticky Bit
find / -perm -1000 -type d 2>/dev/null
The numeric values are straightforward:
- SUID = 4 — prepend to permissions, e.g.
chmod 4755 - SGID = 2 — e.g.
chmod 2755 - Sticky Bit = 1 — e.g.
chmod 1777
Symbolic notation works too:
# Set SUID on an executable
chmod u+s /path/to/binary
# Set SGID on a shared directory
chmod g+s /shared/project
# Set Sticky Bit on a writable directory
chmod +t /shared/uploads
Deep Dive: What Each Bit Actually Does Under the Hood
SUID — Temporarily Become the File Owner
Here’s the classic puzzle: a regular user can run passwd and change their password, but /etc/shadow is owned by root and unreadable by everyone else. How does that work?
ls -l /etc/shadow
# ---------- 1 root shadow 1234 Jun 1 10:00 /etc/shadow
ls -l /usr/bin/passwd
# -rwsr-xr-x 1 root root 68208 Apr 16 2024 /usr/bin/passwd
That s in passwd‘s owner execute slot is SUID. When this binary runs, the kernel temporarily sets the process’s effective UID to the file’s owner — root in this case — instead of the calling user’s UID. The program runs with elevated privileges for as long as it’s executing. When it exits, those privileges vanish.
Security implication: every SUID root binary is a potential privilege escalation target. One exploitable bug — a buffer overflow, a path injection — and an attacker walks away with a root shell. Keep your production SUID list short, and know exactly why each entry is there.
SGID — Group Inheritance That Actually Solves Team Problems
SGID behaves differently depending on whether it targets a file or a directory.
On executables: Same concept as SUID, but uses the file’s group rather than owner. The process’s effective GID becomes the file’s group.
ls -l /usr/bin/write
# -rwxr-sr-x 1 root tty 14328 Jan 20 2024 /usr/bin/write
# 's' in group execute position = SGID
On directories: The directory case is where SGID actually earns its place in real deployments. Any file created inside a SGID directory automatically inherits the directory’s group, regardless of what primary group the creator belongs to.
# Set up a shared project directory for the devteam group
sudo groupadd devteam
sudo mkdir /projects/webapp
sudo chown :devteam /projects/webapp
sudo chmod 2775 /projects/webapp
ls -ld /projects/webapp
# drwxrwsr-x 2 root devteam 4096 Jun 2 09:00 /projects/webapp
# Any file created here now belongs to devteam automatically
touch /projects/webapp/app.py
ls -l /projects/webapp/app.py
# -rw-rw-r-- 1 youruser devteam 0 Jun 2 09:01 app.py
Without SGID, every developer creates files with their own primary group. Nobody else can write to them without a manual chmod after every single file creation. SGID on directories eliminates that friction entirely.
Sticky Bit — Protecting Shared Writable Directories
The problem Sticky Bit solves: if /tmp is world-writable (which it must be), what stops user bob from deleting alice‘s temp files?
ls -ld /tmp
# drwxrwxrwt 18 root root 4096 Jun 2 09:30 /tmp
# The 't' at the end — Sticky Bit
With Sticky Bit set, write permission on the directory no longer means you can delete other people’s files. Only the file owner, the directory owner, or root can remove a file inside that directory.
# alice creates a temp file
touch /tmp/alice_session.sock
# bob tries to remove it — denied
rm /tmp/alice_session.sock
# rm: cannot remove '/tmp/alice_session.sock': Operation not permitted
Advanced Usage: Real Production Scenarios
Combining SGID and Sticky Bit for Team Directories
We ran this exact setup on a production Ubuntu 22.04 server. The result: zero permission-related tickets from developers for months. Nobody needed to remember chown or chmod after creating files — group inheritance and deletion protection just worked.
# SGID(2) + Sticky(1) = 3 — combine them
sudo mkdir /var/shared/team-data
sudo chown root:devteam /var/shared/team-data
sudo chmod 3775 /var/shared/team-data
ls -ld /var/shared/team-data
# drwxrwsr-t 2 root devteam 4096 Jun 2 10:00 /var/shared/team-data
# 's' = SGID (group inherits), 't' = Sticky Bit (owners only can delete)
SUID on Custom Admin Binaries — The Right Way
You might want non-root users to restart a specific service. SUID seems like the answer — but there’s a catch: Linux intentionally ignores SUID on shell scripts. You need a compiled C wrapper.
# SUID on shell scripts is silently ignored
chmod u+s /usr/local/bin/restart-nginx.sh # Does nothing useful
# Correct approach: a minimal C wrapper
cat << 'EOF' > /tmp/restart-nginx.c
#include <unistd.h>
int main() {
setuid(0);
execl("/bin/systemctl", "systemctl", "restart", "nginx", NULL);
return 1;
}
EOF
gcc -o /usr/local/bin/restart-nginx /tmp/restart-nginx.c
chown root:webadmin /usr/local/bin/restart-nginx
chmod 4750 /usr/local/bin/restart-nginx
# Only 'webadmin' group members can execute it, runs as root
Honestly, a targeted sudo rule is cleaner than any custom SUID binary in most situations. Use SUID sparingly — the fewer SUID files on your system, the smaller the attack surface.
Baseline and Diff for Security Audits
# Create a baseline before any system changes
find / -perm /6000 -type f 2>/dev/null | sort > /root/suid_baseline.txt
# After installing packages or patches, compare
find / -perm /6000 -type f 2>/dev/null | sort > /root/suid_current.txt
diff /root/suid_baseline.txt /root/suid_current.txt
# Any new lines = new SUID/SGID binaries added to the system
Run this after every significant package update. A new SUID binary showing up unexpectedly is worth investigating immediately — don’t assume the package manager put it there intentionally.
Practical Tips: Hardening Your System
Strip Unnecessary SUID/SGID Bits
Servers don’t need most of the SUID binaries that ship with a desktop-oriented distribution. Review and strip what isn’t needed:
# Remove SUID from a file
chmod u-s /usr/bin/at
# Remove SGID
chmod g-s /usr/bin/newgrp
# Common review candidates on headless servers:
ls -l /usr/bin/at # Batch job scheduler
ls -l /usr/bin/newgrp # Switch groups
ls -l /usr/bin/chsh # Change login shell
ls -l /usr/bin/chfn # Change user info
Use nosuid Mount Options on Data Partitions
Any partition that doesn’t need SUID executables — upload directories, /tmp, user home partitions — can use the nosuid mount option. It makes SUID and SGID bits on that partition completely inert, even if someone manages to plant a malicious file there:
# /etc/fstab
/dev/sdb1 /data ext4 defaults,nosuid,noexec 0 2
/dev/sdc1 /home ext4 defaults,nosuid 0 2
# Remount live without a reboot
sudo mount -o remount,nosuid /data
Quick Reference: When to Use Each Bit
- SUID on executables: Only when a program genuinely must run as its owner. Every SUID root binary is a potential privilege escalation vector.
- SGID on directories: Shared team directories where consistent group ownership matters. Safe, practical, and commonly useful.
- Sticky Bit on directories: Any world-writable or group-writable shared directory. Standard for
/tmp, upload dirs, and scratch spaces. - SUID on shell scripts: Linux ignores it — use
sudorules or a compiled C wrapper instead.
Doing a pentest or working through a CTF? Finding find, vim, cp, or bash with SUID root is essentially a free privilege escalation — find -exec /bin/sh \; or vim‘s built-in shell escape gets you there in seconds. On production systems, that’s the misconfiguration to catch before anyone else does.

