HackBlue

Tools · Linux command line for security · 02 / 28

How to find SUID and SGID files on Linux and decide which are risky

Understand setuid, setgid, and the sticky bit, list every SUID binary with find, and compare the list to a known-good baseline.

11 min read · practice only with authorization

Three special bits

Setuid (SUID) on an executable makes it run with the file owner’s privileges instead of the caller’s. passwd is SUID root so ordinary users can update /etc/shadow. Setgid (SGID) does the same for the group, and on a directory it makes new files inherit the directory’s group. The sticky bit on a directory, as on /tmp, lets only a file’s owner delete it even when everyone can write the directory.

In ls -l, SUID shows as s in the owner execute position, SGID as s in the group execute position, and sticky as t in the others position. In octal they are 4000, 2000, and 1000.

Snippet
ls -l /usr/bin/passwd /usr/bin/sudo
ls -ld /tmp

List them all

find / -perm -4000 lists files with SUID set, and -perm -2000 finds SGID. Restrict to regular files and skip other filesystems with -xdev so network mounts do not slow you down. On a typical Ubuntu server you will see around 15 to 25 SUID binaries: passwd, su, sudo, mount, umount, newgrp, chsh, and a few helpers.

Any SUID binary is a privilege boundary. A bug in it, or a feature that lets the caller run other commands, becomes root access. That is why a shell, a text editor, or an interpreter like python with SUID root is an immediate escalation path.

Snippet
sudo find / -xdev -type f -perm -4000 -printf "%M %u %p\n" 2>/dev/null | sort -k3
sudo find / -xdev -type f -perm -2000 -printf "%M %g %p\n" 2>/dev/null

Lab: baseline and detect a change

Save the SUID list from a fresh lab VM as a baseline. Then simulate a backdoor by copying /bin/bash to /usr/local/bin/helper and setting chmod 4755 on it as root. Rerun the find and diff the two lists. The new entry jumps out.

Remove the test file afterward. In production, run the same comparison on a schedule or let a file integrity tool like AIDE watch permissions, and check every new entry against package ownership with dpkg -S or rpm -qf.

Snippet
sudo find / -xdev -type f -perm -4000 2>/dev/null | sort > suid-baseline.txt
sudo cp /bin/bash /usr/local/bin/helper && sudo chmod 4755 /usr/local/bin/helper
sudo find / -xdev -type f -perm -4000 2>/dev/null | sort | diff suid-baseline.txt -
dpkg -S /usr/local/bin/helper   # no package owns it
sudo rm /usr/local/bin/helper

Reduce the list

Remove SUID from tools nobody needs, for example chmod u-s on a legacy utility, but only after confirming what depends on it. Mount user-writable filesystems such as /tmp and /home with the nosuid option so SUID bits there are ignored.

Prefer capabilities for narrow needs. ping used to be SUID root and now typically uses the cap_net_raw capability, which grants only raw sockets.

Diagram

SUID audit in four steps

  1. List

    Find every SUID and SGID file on local filesystems.

    Command
    sudo find / -xdev -type f -perm -4000
  2. Attribute

    Check package ownership with dpkg -S or rpm -qf.

  3. Compare

    Diff against a known-good baseline.

  4. Reduce

    Remove unneeded bits, mount nosuid, prefer capabilities.

Check yourself

1. What does SUID on an executable do?

2. Which find expression lists SUID files?

3. You find /usr/local/bin/helper SUID root and no package owns it. What is the best interpretation?