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.
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.
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.
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.
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
- List
Find every SUID and SGID file on local filesystems.
sudo find / -xdev -type f -perm -4000
- Attribute
Check package ownership with dpkg -S or rpm -qf.
- Compare
Diff against a known-good baseline.
- 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?