Description
A program with the setuid (or setgid) bit runs with the rights of its owner (or group), usually
root, whoever starts it. Every such program on local filesystems is known and needed. The
check lists the ones that do not match {{ allowed }}, a list of shell patterns that holds the
setuid and setgid programs shipped by common Ubuntu packages. A human still reviews the full list.
Fresh Ubuntu cloud images have 20 to 25 such programs. They are all in the default list except
/usr/bin/ntfs-3g on 26.04, which the image ships setuid root: NTFS support is rarely needed on
a server, and its FUSE driver running as root has had several privilege escalation flaws, so it
is reported for review rather than accepted. On 26.04 sudo is sudo-rs
(/usr/lib/cargo/bin/sudo and su), with the original sudo kept as /usr/bin/sudo.ws.
Graphical workstations add a few more (for example Xorg.wrap, pppd): add the ones that are
needed to the tailoring file.
dpkg-statoverride keeps a removed bit across package upgrades; a plain chmod is undone at
the next upgrade of the package. The check passes as "review": it confirms there is nothing
unexpected, not that every listed program is needed.
Rationale
Setuid root programs are the main way for a local user to escalate privileges: a bug in any of them (pkexec, sudo and others have had several) gives root. Attackers also leave a setuid copy of a shell behind as a backdoor. Fewer such programs mean less attack surface, and a reviewed list makes a new one stand out.
Impact
Removing the setuid bit from a program stops non-root users from using it (for example chfn,
chsh or newgrp). Check who needs each program before removing it.
Parameters
| Parameter | Core | Server | Server hardened | Desktop | Desktop hardened | Bounds |
|---|---|---|---|---|---|---|
allowedlist. Shell patterns of setuid and setgid programs accepted without review. | /usr/bin/at, /usr/bin/chage, /usr/bin/chfn, /usr/bin/chsh, /usr/bin/crontab, /usr/bin/dotlockfile, /usr/bin/expiry, /usr/bin/fusermount, /usr/bin/fusermount3, /usr/bin/gpasswd, /usr/bin/mount, /usr/bin/newgrp, /usr/bin/passwd, /usr/bin/pkexec, /usr/bin/ssh-agent, /usr/bin/su, /usr/bin/sudo, /usr/bin/sudo.ws, /usr/bin/umount, /usr/bin/wall, /usr/bin/write, /usr/bin/bsd-write, /usr/lib/dbus-1.0/dbus-daemon-launch-helper, /usr/lib/openssh/ssh-keysign, /usr/lib/polkit-1/polkit-agent-helper-1, /usr/lib/*/utempter/utempter, /usr/lib/cargo/bin/su, /usr/lib/cargo/bin/sudo, /usr/lib/eject/dmcrypt-get-device, /usr/lib/policykit-1/polkit-agent-helper-1, /usr/lib/snapd/snap-confine, /usr/libexec/polkit-agent-helper-1, /usr/lib/xorg/Xorg.wrap, /usr/sbin/exim4, /usr/sbin/pam_extrausers_chkpwd, /usr/sbin/unix_chkpwd | /usr/bin/at, /usr/bin/chage, /usr/bin/chfn, /usr/bin/chsh, /usr/bin/crontab, /usr/bin/dotlockfile, /usr/bin/expiry, /usr/bin/fusermount, /usr/bin/fusermount3, /usr/bin/gpasswd, /usr/bin/mount, /usr/bin/newgrp, /usr/bin/passwd, /usr/bin/pkexec, /usr/bin/ssh-agent, /usr/bin/su, /usr/bin/sudo, /usr/bin/sudo.ws, /usr/bin/umount, /usr/bin/wall, /usr/bin/write, /usr/bin/bsd-write, /usr/lib/dbus-1.0/dbus-daemon-launch-helper, /usr/lib/openssh/ssh-keysign, /usr/lib/polkit-1/polkit-agent-helper-1, /usr/lib/*/utempter/utempter, /usr/lib/cargo/bin/su, /usr/lib/cargo/bin/sudo, /usr/lib/eject/dmcrypt-get-device, /usr/lib/policykit-1/polkit-agent-helper-1, /usr/lib/snapd/snap-confine, /usr/libexec/polkit-agent-helper-1, /usr/lib/xorg/Xorg.wrap, /usr/sbin/exim4, /usr/sbin/pam_extrausers_chkpwd, /usr/sbin/unix_chkpwd | /usr/bin/at, /usr/bin/chage, /usr/bin/chfn, /usr/bin/chsh, /usr/bin/crontab, /usr/bin/dotlockfile, /usr/bin/expiry, /usr/bin/fusermount, /usr/bin/fusermount3, /usr/bin/gpasswd, /usr/bin/mount, /usr/bin/newgrp, /usr/bin/passwd, /usr/bin/pkexec, /usr/bin/ssh-agent, /usr/bin/su, /usr/bin/sudo, /usr/bin/sudo.ws, /usr/bin/umount, /usr/bin/wall, /usr/bin/write, /usr/bin/bsd-write, /usr/lib/dbus-1.0/dbus-daemon-launch-helper, /usr/lib/openssh/ssh-keysign, /usr/lib/polkit-1/polkit-agent-helper-1, /usr/lib/*/utempter/utempter, /usr/lib/cargo/bin/su, /usr/lib/cargo/bin/sudo, /usr/lib/eject/dmcrypt-get-device, /usr/lib/policykit-1/polkit-agent-helper-1, /usr/lib/snapd/snap-confine, /usr/libexec/polkit-agent-helper-1, /usr/lib/xorg/Xorg.wrap, /usr/sbin/exim4, /usr/sbin/pam_extrausers_chkpwd, /usr/sbin/unix_chkpwd | /usr/bin/at, /usr/bin/chage, /usr/bin/chfn, /usr/bin/chsh, /usr/bin/crontab, /usr/bin/dotlockfile, /usr/bin/expiry, /usr/bin/fusermount, /usr/bin/fusermount3, /usr/bin/gpasswd, /usr/bin/mount, /usr/bin/newgrp, /usr/bin/passwd, /usr/bin/pkexec, /usr/bin/ssh-agent, /usr/bin/su, /usr/bin/sudo, /usr/bin/sudo.ws, /usr/bin/umount, /usr/bin/wall, /usr/bin/write, /usr/bin/bsd-write, /usr/lib/dbus-1.0/dbus-daemon-launch-helper, /usr/lib/openssh/ssh-keysign, /usr/lib/polkit-1/polkit-agent-helper-1, /usr/lib/*/utempter/utempter, /usr/lib/cargo/bin/su, /usr/lib/cargo/bin/sudo, /usr/lib/eject/dmcrypt-get-device, /usr/lib/policykit-1/polkit-agent-helper-1, /usr/lib/snapd/snap-confine, /usr/libexec/polkit-agent-helper-1, /usr/lib/xorg/Xorg.wrap, /usr/sbin/exim4, /usr/sbin/pam_extrausers_chkpwd, /usr/sbin/unix_chkpwd | /usr/bin/at, /usr/bin/chage, /usr/bin/chfn, /usr/bin/chsh, /usr/bin/crontab, /usr/bin/dotlockfile, /usr/bin/expiry, /usr/bin/fusermount, /usr/bin/fusermount3, /usr/bin/gpasswd, /usr/bin/mount, /usr/bin/newgrp, /usr/bin/passwd, /usr/bin/pkexec, /usr/bin/ssh-agent, /usr/bin/su, /usr/bin/sudo, /usr/bin/sudo.ws, /usr/bin/umount, /usr/bin/wall, /usr/bin/write, /usr/bin/bsd-write, /usr/lib/dbus-1.0/dbus-daemon-launch-helper, /usr/lib/openssh/ssh-keysign, /usr/lib/polkit-1/polkit-agent-helper-1, /usr/lib/*/utempter/utempter, /usr/lib/cargo/bin/su, /usr/lib/cargo/bin/sudo, /usr/lib/eject/dmcrypt-get-device, /usr/lib/policykit-1/polkit-agent-helper-1, /usr/lib/snapd/snap-confine, /usr/libexec/polkit-agent-helper-1, /usr/lib/xorg/Xorg.wrap, /usr/sbin/exim4, /usr/sbin/pam_extrausers_chkpwd, /usr/sbin/unix_chkpwd |
The value used is your override (tailoring file), else the profile's, else the one of the profile it extends, else the default. Bounds limit what an override may set.
Audit
- For each local filesystem listed by
findmnt -knr -o TARGET -t ext2,ext3,ext4,xfs,btrfs,jfs,f2fs,vfat,exfat,tmpfs, runfind <mount point> -xdev -type f -perm /6000 -exec ls -l {} +. - For each program, check that it comes from a package (
dpkg -S <program>), thatdpkg --verify <package>reports no change, and that it is needed on this system.
Automated: every check must pass. Scanners report each check by its id.
unexpected
Setuid and setgid files on local filesystems that match none of the allowed patterns.
script: |
all=$(for m in $(findmnt -knr -o TARGET -t ext2,ext3,ext4,xfs,btrfs,jfs,f2fs,vfat,exfat,tmpfs); do
find "$m" -xdev -type f -perm /6000 -print 2>/dev/null
done | sort -u)
set -f
rc=0 n=0
for f in $all; do
n=$((n + 1))
ok=0
for p in $CB_ALLOWED; do
# shellcheck disable=SC2254 # the pattern is the point
case $f in $p) ok=1; break ;; esac
done
[ "$ok" = 1 ] && continue
echo "unexpected: $(stat -c '%A %U:%G' "$f") $f"
rc=1
done
echo "$n setuid/setgid file(s) in total"
exit $rc
Remediation
For each unexpected program, and for listed programs that this system does not need:
- If it belongs to no package (
dpkg -Sfinds nothing) ordpkg --verifyreports it as changed, treat it as a possible compromise and investigate before deleting it. - If its package is not needed, purge the package.
- Otherwise remove the bit in a way that survives upgrades:
dpkg-statoverride --update --add root root 0755 <program>. - Add programs that are needed and reviewed to
{{ allowed }}in the tailoring file.
References
- dpkg-statoverride(1), Ubuntu manual pages Canonical, vendor-doc, accessed 2026-10-19
- inode(7), Ubuntu manual pages (set-user-ID and set-group-ID bits) Canonical, vendor-doc, accessed 2026-10-19
- ANSSI-BP-028 v2.0 - Recommandations de configuration d'un système GNU/Linux (R56, R57) ANSSI, guide, accessed 2026-10-19
Mappings
| Framework | ID | Relation | Note |
|---|---|---|---|
| ANSSI-BP-028 - Recommandations de configuration d'un système GNU/Linux2.0 | R56 | equivalent | |
| ANSSI-BP-028 - Recommandations de configuration d'un système GNU/Linux2.0 | R57 | partial | R57 asks to remove the setuid root bit wherever possible; ours requires the review, the removal stays a local decision. |
| NIST SP 800-53 Rev. 55.1.1 | AC-6 | related | |
| NIST SP 800-53 Rev. 55.1.1 | CM-7 | related |
How this control relates to the other framework's: equivalent, stricter, looser, partial or related.
Threats mitigated
People and lifecycle
| Authors |
|
| Verified | 2026-10-19 by Sn0wAlice on 20.04, Proxmox VM 2520, Ubuntu 20.04 cloud image, kernel 5.4.0-216-generic. Review on fresh install (25 files, all allowed); setuid copy in /usr/local/bin reported as unexpected. |
| Verified | 2026-10-19 by Sn0wAlice on 22.04, Proxmox VM 2522, Ubuntu 22.04 cloud image, kernel 5.15.0-198-generic. Review on fresh install (21 files, all allowed); setuid copy in /usr/local/bin reported as unexpected. |
| Verified | 2026-10-19 by Sn0wAlice on 24.04, Proxmox VM 2524, Ubuntu 24.04 cloud image, kernel 6.8.0-147-generic. Review on fresh install (20 files, all allowed). |
| Verified | 2026-10-19 by Sn0wAlice on 26.04, Proxmox VM 2526, Ubuntu 26.04 cloud image, kernel 7.0.0-38-generic. Fails on fresh install: /usr/bin/ntfs-3g setuid root reported as unexpected (21 files, sudo-rs paths allowed); setuid copy in /usr/local/bin reported too. |
| Introduced in | v0.1.0 |
| Created, updated | 2026-10-19, 2026-10-19 |
| Translations | fr |