Understanding SUID, SGID, and the Sticky Bit
AI generated
$
/etc
Linux · Filesystem · Security · Server Administration
Understanding SUID, SGID, and the Sticky Bit
How special permission bits secure, and endanger, Linux systems

SUID, SGID, and the sticky bit control which rights a program runs with, who inherits group ownership, and who is allowed to delete files in shared directories. Used correctly, they enable safe system tools like passwd and stable shared project folders without constant manual permission fixes. Misconfigured, unnoticed SUID binaries open a classic path to privilege escalation that every administrator should audit regularly.

14 min read SUID · SGID · Sticky Bit · Privilege Escalation chmod · find · auditd · GTFOBins

1. What special permission bits actually control

The classic rwx permissions for owner, group, and others do not cover every everyday scenario. How is an ordinary user supposed to change their own password when that requires write access to the root-protected file /etc/shadow? How can a team work together on a project directory without manually fixing the group ownership of new files after every commit? How do you stop users from deleting each other's files in a world-writable directory like /tmp? Exactly these three problems are what the special permission bits SUID, SGID, and the sticky bit exist to solve.

Technically, all three bits are part of the same twelve-bit permission field on an inode that carries a leading bit triplet in addition to the nine standard rwx bits. In the output of ls -l this shows up as an s in the owner-execute position for SUID, an s in the group-execute position for SGID, or a t in the other-execute position for the sticky bit. In octal notation it is written as a fourth, leading digit: 4 for SUID, 2 for SGID, 1 for the sticky bit, combinable by addition. chmod 4755 file therefore sets SUID on top of rwxr-xr-x. The three mechanisms are independent of each other and behave differently depending on the file type, which is why they should never be treated as a single, uniform concept.

2. SUID in detail: run-as-owner

When the SUID bit is set on an executable file, the kernel changes the effective UID (EUID) of the resulting process to the file's owner during execve(), instead of leaving it at the real UID of the calling user. All subsequent permission checks in the kernel, such as opening files, are based on the effective UID rather than the real one. The classic example is /usr/bin/passwd, which is owned by root and has SUID set: an ordinary user launches passwd with their own UID, but the process internally runs with root privileges and can therefore update the user's own entry in /etc/shadow without that file having to be writable by everyone.

Two restrictions matter here. First, SUID has no effect whatsoever on directories, it only applies to executable files. Second, the Linux kernel deliberately ignores the SUID bit on interpreted scripts that start with a shebang line, for historical security reasons: a classic race condition between opening the script file and starting the interpreter used to be exploitable, so modern kernels simply run #!/bin/bash scripts without elevated privileges even if SUID is set. SUID therefore only works reliably on compiled ELF binaries.

3. SGID in detail: run-as-group and directory inheritance

The SGID bit on an executable file works analogously to SUID, but for the group: the kernel sets the process's effective GID (EGID) to the file's group instead of the calling user's primary group. One example is wall, which gets write access to other users' terminal devices through the tty group, without every user having to be a direct member of that group.

Far more commonly, SGID is used on directories, where it behaves completely differently than on files: new files and subdirectories created inside an SGID directory automatically inherit its group ownership instead of the creator's primary group. In addition, newly created subdirectories propagate the SGID bit further themselves, so group inheritance is preserved recursively across the entire directory tree. That makes SGID the central tool for directories shared by multiple users.

4. Sticky bit: delete protection in shared directories

Historically, the sticky bit on an executable file caused its program text to remain in swap after termination, to speed up subsequent invocations. That behavior is meaningless on modern Linux systems, the kernel ignores the sticky bit on files entirely. Today it is relevant only on directories: when set, a file inside the directory can only be deleted, renamed, or moved by the file's owner, the directory's owner, or root, even if the directory itself is writable by everyone.

The best-known example is /tmp with the mode drwxrwxrwt, octal 1777. Any user can create their own temporary files there, but nobody can delete or overwrite another user's temporary files just because the directory is globally writable. Without the sticky bit, /tmp would be a trivial target for denial-of-service attacks by deleting other users' lock and socket files. systemd-tmpfiles also enforces this mode on every boot through configuration files under /usr/lib/tmpfiles.d/, so manual misconfigurations get corrected automatically.

5. Setting the bits: chmod symbolic and octal

All three bits can be set either symbolically or in octal notation. Symbolically you use chmod u+s for SUID, chmod g+s for SGID, and chmod +t for the sticky bit, each combinable with normal rwx operations. In octal notation, the fourth, leading digit is used: 4 for SUID, 2 for SGID, 1 for the sticky bit. The values can be added together, so chmod 6755 sets both SUID and SGID on a file that also has rwxr-xr-x.

Setting these bits requires either root privileges or ownership of the file, and many systems automatically strip the SUID bit when a file's ownership changes via chown, for security reasons. An uppercase S or T instead of a lowercase s or t in ls -l output signals an inconsistent state: the special bit is set, but the corresponding execute bit is missing, which for SUID files is usually a configuration mistake, since a non-executable SUID binary has no effect at all.


#!/usr/bin/env bash
# Special permission bits: symbolic and octal notation

# SUID symbolically, on a compiled binary owned by root
sudo chmod u+s /usr/local/bin/backup-agent
ls -l /usr/local/bin/backup-agent
# -rwsr-xr-x 1 root root 48200 Jul 12 09:00 /usr/local/bin/backup-agent

# SUID + SGID + rwxr-xr-x in one octal command
sudo chmod 6755 /usr/local/bin/backup-agent

# SGID on a shared team directory (not a file!)
sudo mkdir -p /srv/www/project
sudo chown root:developers /srv/www/project
sudo chmod 2775 /srv/www/project
ls -ld /srv/www/project
# drwxrwsr-x 2 root developers 4096 Jul 12 09:01 /srv/www/project

# Sticky bit on a shared upload directory
sudo mkdir -p /srv/uploads
sudo chmod 1777 /srv/uploads
ls -ld /srv/uploads
# drwxrwxrwt 2 root root 4096 Jul 12 09:02 /srv/uploads

# Remove a bit again
sudo chmod u-s /usr/local/bin/backup-agent

6. Legitimate use cases: passwd, /tmp, and team directories

Besides passwd, tools like mount, su, and historically ping classically used SUID root because they either write protected configuration files or need to open raw sockets, neither of which is possible without elevated privileges. Modern distributions increasingly replace SUID root on ping with Linux capabilities instead: setcap cap_net_raw+ep /bin/ping grants exactly the one required right without giving the program full root access. That significantly reduces the attack surface, since a bug in ping no longer automatically leads to a complete system takeover. For passwd, an equivalent replacement is hardly possible, since the program generically needs to write to /etc/shadow.

For team directories, the standard approach is combining SGID with a dedicated Unix group: a directory such as /srv/www/project belongs to the developers group and carries SGID, so every new file is automatically assigned to the correct group regardless of which developer created it. Adding default ACLs via setfacl -d -m g:developers:rwx /srv/www/project lets you specify granular rwx permissions for new files as well, going beyond the plain group inheritance that SGID alone provides.


#!/usr/bin/env bash
# Replace SUID-root with a narrow capability where possible
sudo setcap cap_net_raw+ep /bin/ping
getcap /bin/ping
# /bin/ping cap_net_raw=ep

# Verify ping no longer needs SUID at all
ls -l /bin/ping
# -rwxr-xr-x 1 root root 71000 Jul 12 09:05 /bin/ping

# Shared team directory with SGID and a default ACL
sudo groupadd -f developers
sudo mkdir -p /srv/www/project
sudo chgrp developers /srv/www/project
sudo chmod 2775 /srv/www/project
sudo setfacl -d -m g:developers:rwx /srv/www/project

# Any file created here belongs to "developers" automatically
sudo -u alice touch /srv/www/project/index.php
stat -c '%U:%G %A' /srv/www/project/index.php
# alice:developers -rw-rw-r--

7. SUID as an attack vector: privilege escalation

A SUID-root binary that can do more than its actual task requires is a classic target for privilege escalation. The typical attack flow: an attacker gains a low-privilege shell, for example through a vulnerable web application, and then systematically searches for SUID-root binaries that can be abused. The GTFOBins project catalogs exactly these cases: numerous standard tools such as find, vim, nmap, or less have built-in functions to spawn a subshell, and that subshell automatically inherits the effective UID of the file's owner when the SUID bit is set.

A real-world example: if an administrator accidentally runs chmod u+s /usr/bin/find, the command find . -exec /bin/sh -p \; -quit is enough to obtain a root shell, because find starts its -exec child process with its own effective UID. Similar patterns exist for environment variable attacks like LD_PRELOAD, where an attacker injects a custom shared library into a SUID binary, as well as for symlink attacks against programs that handle paths unsafely. The operational consequence: general-purpose tools should never carry SUID root, more granular mechanisms such as sudo with tightly scoped command lists or capabilities are almost always the safer choice.


#!/usr/bin/env bash
# Demonstrating why an unnecessary SUID bit is dangerous
# (run only in an isolated lab environment, never in production)

# Misconfiguration: administrator sets SUID root on a general tool
sudo chmod u+s /usr/bin/find
ls -l /usr/bin/find
# -rwsr-xr-x 1 root root 316280 Jul 12 09:10 /usr/bin/find

# Attacker with a low-privilege shell escalates instantly
find . -exec /bin/sh -p \; -quit
# id
# uid=1000(attacker) euid=0(root) gid=1000(attacker) groups=1000(attacker)

# The fix: remove SUID from general-purpose tools immediately
sudo chmod u-s /usr/bin/find

8. Auditing and monitoring SUID binaries

The first step of any audit is a complete inventory: find / -xdev \( -perm -4000 -o -perm -2000 \) -type f -exec ls -la {} \; 2>/dev/null lists every file with the SUID or SGID bit set on the current filesystem. The -xdev option prevents mounted network shares or external drives from being searched unnecessarily. Right after a fresh install or hardening pass, this list should be saved as a baseline, for example as a sha256sum hash, to reliably detect later deviations.

For ongoing monitoring, auditd is the tool of choice: a rule such as auditctl -w /usr -p wa -k suid_watch logs every write or attribute access under /usr in the audit log, so a newly set SUID bit shows up immediately with a timestamp and the executing user. Package managers also offer a quick comparison against the expected values from the package itself: debsums -c on Debian/Ubuntu or rpm -Va on RHEL-based systems flag files whose mode deviates from the package original. Tools like AIDE go even further and track permission changes as part of a full filesystem integrity check with a daily report.


{
  "scan_id": "suid-audit-2026-07-12T06-00-00Z",
  "host": "web-prod-03",
  "baseline_file": "/var/lib/suid-audit/baseline.sha256",
  "findings": [
    {
      "path": "/usr/bin/passwd",
      "mode": "4755",
      "owner": "root",
      "group": "root",
      "in_baseline": true,
      "status": "expected"
    },
    {
      "path": "/usr/bin/find",
      "mode": "4755",
      "owner": "root",
      "group": "root",
      "in_baseline": false,
      "status": "new_suid_detected",
      "severity": "critical"
    }
  ],
  "summary": { "total_suid": 24, "total_sgid": 9, "new_since_baseline": 1 }
}

9. Best practices and system hardening compared

Beyond simply hunting down existing SUID binaries, preventive hardening is a standard part of the job: filesystems that ordinary users can write to should generally be mounted with the nosuid option, such as /home, /tmp, and /var/tmp in /etc/fstab. The kernel then ignores the SUID bit on those filesystems entirely, even if an attacker manages to upload a SUID file there. This line of defense works in addition to fixing the root cause and renders many exploit chains ineffective from the outset.

Automating this through configuration management ensures hardening rules are enforced continuously across an entire server fleet, not just once. An Ansible task can regularly check whether new, unapproved SUID files have appeared, and automatically alert or even remove the bit on deviation.


# Ansible task: audit SUID/SGID binaries against an approved allowlist
- name: Find all SUID and SGID files on the host
  ansible.builtin.shell: >
    find / -xdev \( -perm -4000 -o -perm -2000 \) -type f 2>/dev/null
  register: suid_scan
  changed_when: false

- name: Compare findings against the approved allowlist
  ansible.builtin.set_fact:
    unexpected_suid: "{{ suid_scan.stdout_lines | difference(approved_suid_allowlist) }}"

- name: Fail the play if unapproved SUID binaries are found
  ansible.builtin.fail:
    msg: "Unapproved SUID/SGID binaries detected: {{ unexpected_suid }}"
  when: unexpected_suid | length > 0

- name: Mount user-writable filesystems with nosuid
  ansible.posix.mount:
    path: "{{ item }}"
    opts: "defaults,nosuid,nodev"
    state: mounted
  loop:
    - /tmp
    - /var/tmp
    - /home
Scenario Unsafe Recommended Benefit
Shared temp directory chmod 777 without sticky bit chmod 1777 Only the owner can delete their own files
Privileged deploy script chmod 4777 deploy.sh chmod 4750 + dedicated group Execution limited to an authorized group
Shared project directory Manual chgrp after every commit chmod 2775 with SGID New files inherit the group automatically
Searching for SUID files One-off, unscheduled manual check systemd timer + baseline diff New SUID binaries are caught immediately
Network rights for ping SUID root on the entire binary setcap cap_net_raw+ep Smaller attack surface, granular rights

The table shows a recurring pattern: in almost every case, a more precise mechanism exists than crudely setting a special bit on a fully exposed target. Consistently applying these recommendations, combined with enforcing nosuid on every user-writable filesystem, meaningfully reduces the attack surface without restricting legitimate use cases like passwd or team directories.

Mironsoft

Server hardening, permission audits, and security concepts for Linux infrastructure

Unnoticed SUID binaries on your servers?

We audit SUID and SGID permissions across your Linux servers, remove unnecessary special bits, and set up continuous monitoring with auditd and baseline comparisons, so new risks get caught immediately.

Permission audit

Complete inventory of every SUID and SGID file on the system

Hardening

nosuid mounts, capabilities instead of SUID root, minimal attack surface

Monitoring

auditd rules and automated baseline comparisons in the config-management pipeline

10. Summary

SUID, SGID, and the sticky bit solve three distinct but related problems in the Linux permission model. SUID lets a program run with the rights of its owner instead of the calling user, indispensable for tools like passwd. SGID applies the same idea to groups and, on directories, adds an especially useful extra behavior: automatically inheriting group ownership for new files. The sticky bit protects shared, world-writable directories such as /tmp from users deleting each other's files.

At the same time, SUID-root binaries in particular are a favorite target for privilege escalation whenever they carry more functionality than their actual purpose requires. Regularly comparing against a baseline, monitoring with auditd, consistently preferring Linux capabilities over full SUID root, and mounting user-writable filesystems with nosuid together form a robust line of defense, without restricting the legitimate uses of these bits.

SUID, SGID, and the Sticky Bit, the essentials at a glance

SUID: run-as-owner

The process runs with the effective UID of the file's owner. Only effective on ELF binaries, not on shebang scripts.

SGID: run-as-group & inheritance

Like SUID for files, but for groups. On directories, new files automatically inherit the directory's group.

Sticky bit: delete protection

chmod 1777 for /tmp-style directories. Only the owner, directory owner, or root may delete files.

Audit & hardening

Combine find -perm -4000, auditd watches, baseline comparisons, and nosuid mounts.

11. FAQ: SUID, SGID, and the Sticky Bit

1What does the SUID bit do on an executable file?
The kernel sets the process's effective UID to the file's owner, not to the calling user's UID. This lets programs temporarily run with someone else's rights, usually root.
2What is the difference between SUID and SGID?
SUID affects the effective UID, SGID the effective GID. On directories, SGID additionally makes new files inherit the directory's group automatically.
3What is the sticky bit typically used for?
For shared, world-writable directories like /tmp: only the file owner, directory owner, or root may delete or rename files there.
4How do I set SUID, SGID, and the sticky bit with chmod?
Symbolically: chmod u+s, g+s, +t. Octal via a fourth digit: 4 for SUID, 2 for SGID, 1 for the sticky bit, e.g. chmod 4755.
5Why is passwd the classic example of legitimate SUID use?
passwd is owned by root and carries SUID so users can update their own entry in /etc/shadow, without that file being writable by everyone.
6Why are SUID binaries a popular privilege escalation target?
Tools like find or vim can spawn a subshell that automatically inherits root rights when SUID root is set. GTFOBins catalogs these cases systematically.
7How do I find all SUID files on a system?
find / -xdev -perm -4000 -type f 2>/dev/null for SUID, -perm -2000 for SGID. Save the result as a baseline and compare regularly.
8What does an uppercase S or T instead of lowercase mean?
Inconsistent state: the special bit is set, but the execute bit is missing. Usually a configuration mistake, the bit then has no effect.
9Should I use SUID on shell scripts?
No. Modern kernels ignore SUID on shebang scripts due to a historical race condition. It works reliably only on compiled ELF binaries.
10How do I continuously monitor for new SUID binaries?
With auditd watches, regular systemd-timer scans, and automated comparison against a baseline or allowlist in Ansible.