Recent from talks
Nothing was collected or created yet.
Setuid
View on WikipediaIn Unix-like systems, the access rights flags setuid and setgid (short for set user identity and set group identity)[1] allow users to run an executable with the file system permissions of the executable's owner or group respectively and to change behaviour in directories. They are often used to allow users on a computer system to run programs with temporarily elevated privileges to perform a specific task. While the assumed user id or group id privileges provided are not always elevated, at a minimum they are specific.
The flags setuid and setgid are needed for tasks that require different privileges than what the user is normally granted, such as the ability to alter system files or databases to change their login password.[2] Some of the tasks that require additional privileges may not immediately be obvious, though, such as the ping command, which must send and listen for control packets on a network interface.
File modes
[edit]The setuid and setgid bits are normally represented as the values 4 for setuid and 2 for setgid in the high-order octal digit of the file mode. For example, 6711 has both the setuid and setgid bits (4 + 2 = 6) set, and also the file read/write/executable for the owner (7), and executable by the group (first 1) and others (second 1). Most implementations have a symbolic representation of these bits; in the previous example, this could be u=rwx,go=x,ug+s.
Typically, chmod does not have a recursive mode restricted to directories, so modifying an existing directory tree must be done manually, with a command such as find /path/to/directory -type d -exec chmod g+s '{}' '\'.
Effects
[edit]The setuid and setgid flags have different effects, depending on whether they are applied to a file, to a directory or binary executable or non-binary executable file. The setuid and setgid flags have an effect only on binary executable files and not on scripts (e.g., Bash, Perl, Python).[3]
When set on an executable file
[edit]When the setuid or setgid attributes are set on an executable file, then any users able to execute the file will automatically execute the file with the privileges of the file's owner (commonly root) and/or the file's group, depending upon the flags set.[2] This allows the system designer to permit trusted programs to be run which a user would otherwise not be allowed to execute. These may not always be obvious. For example, the ping command may need access to networking privileges that a normal user cannot access; therefore it may be given the setuid flag to ensure that a user who needs to ping another system can do so, even if their account does not have the required privilege for sending packets.
Security impact
[edit]For security purposes, the invoking user is usually prohibited by the system from altering the new process in any way, such as by using ptrace, LD_LIBRARY_PATH or sending signals to it, to exploit the raised privilege, although signals from the terminal will still be accepted.
While the setuid feature is very useful in many cases, its improper use can pose a security risk[2] if the setuid attribute is assigned to executable programs that are not carefully designed. Due to potential security issues,[4] many operating systems ignore the setuid attribute when applied to executable shell scripts.[citation needed]
The presence of setuid executables explains why the chroot system call is not available to non-root users on Unix. See limitations of chroot for more details.
When set on a directory
[edit]Setting the setgid permission on a directory causes files and subdirectories created within to inherit its group ownership, rather than the primary group of the file-creating process. Created subdirectories also inherit the setgid bit. The policy is only applied during creation and, thus, only prospectively. Directories and files existing when the setgid bit is applied are unaffected, as are directories and files moved into the directory on which the bit is set.
Thus is granted a capacity to work with files amongst a group of users without explicitly setting permissions, but limited by the security model expectation that existing files permissions do not implicitly change.
The setuid permission set on a directory is ignored on most UNIX and Linux systems.[5][citation needed] However FreeBSD can be configured to interpret setuid in a manner similar to setgid, in which case it forces all files and sub-directories created in a directory to be owned by that directory's owner - a simple form of inheritance.[6] This is generally not needed on most systems derived from BSD, since by default directories are treated as if their setgid bit is always set, regardless of the actual value. As is stated in open(2), "When a new file is created it is given the group of the directory which contains it."[7]
Examples
[edit]Checking permissions
[edit]Permissions of a file can be checked in octal form and/or alphabetic form with the command line tool stat
[ torvalds ~ ] $ stat -c "%a %A" ~/test/
1770 drwxrwx--T
SUID
[edit]4701 on an executable file owned by 'root' and the group 'root'
A user named 'thompson' attempts to execute the file. The executable permission for all users is set (the '1') so 'thompson' can execute the file. The file owner is 'root' and the SUID permission is set (the '4') - so the file is executed as 'root'.
The reason an executable would be run as 'root' is so that it can modify specific files that the user would not normally be allowed to, without giving the user full root access.
A default use of this can be seen with the /usr/bin/passwd binary file. /usr/bin/passwd needs to modify /etc/passwd and /etc/shadow which store account information and password hashes for all users, and these can only be modified by the user 'root'.
[ thompson ~ ] $ stat -c "%a %U:%G %n" /usr/bin/passwd
4701 root:root /usr/bin/passwd
[ thompson ~ ] $ passwd
passwd: Changing password for thompson
The owner of the process is not the user running the executable file but the owner of the executable file
SGID
[edit]2770 on a directory named 'music' owned by the user 'root' and the group 'engineers'
A user named 'torvalds' who belongs primarily to the group 'torvalds' but secondarily to the group 'engineers' makes a directory named 'electronic' under the directory named 'music'. The group ownership of the new directory named 'electronic' inherits 'engineers.' This is the same when making a new file named 'imagine.txt'
Without SGID the group ownership of the new directory/file would have been 'torvalds' as that is the primary group of user 'torvalds'.
[ torvalds ~ ] $ groups torvalds
torvalds : torvalds engineers
[ torvalds ~ ] $ stat -c "%a %U:%G %n" ./music/
2770 root:engineers ./music/
[ torvalds ~ ] $ mkdir ./music/electronic
[ torvalds ~ ] $ stat -c "%U:%G %n" ./music/electronic/
torvalds:engineers ./music/electronic/
[ torvalds ~ ] $ echo 'NEW FILE' > ./music/imagine.txt
[ torvalds ~ ] $ stat -c "%U:%G %n" ./music/imagine.txt
torvalds:engineers ./music/imagine.txt
[ torvalds ~ ] $ touch ~/test
[ torvalds ~ ] $ stat -c "%U:%G %n" ~/test
torvalds:torvalds ~/test
Sticky bit
[edit]1770 on a directory named 'videogames' owned by the user 'torvalds' and the group 'engineers'.
A user named 'torvalds' creates a file named 'tekken' under the directory named 'videogames'. A user named 'wozniak', who is also part of the group 'engineers', attempts to delete the file named 'tekken' but he cannot, since he is not the owner.
Without sticky bit, 'wozniak' could have deleted the file, because the directory named 'videogames' allows read and write by 'engineers'. A default use of this can be seen at the /tmp folder.
[ torvalds /home/shared/ ] $ groups torvalds
torvalds : torvalds engineers
[ torvalds /home/shared/ ] $ stat -c "%a %U:%G %n" ./videogames/
1770 torvalds:engineers ./videogames/
[ torvalds /home/shared/ ] $ echo 'NEW FILE' > videogames/tekken
[ torvalds /home/shared/ ] $ su - wozniak
Password:
[ wozniak ~/ ] $ groups wozniak
wozniak : wozniak engineers
[ wozniak ~/ ] $ cd /home/shared/videogames
[ wozniak /home/shared/videogames/ ] $ rm tekken
rm: cannot remove ‘tekken’: Operation not permitted
Sticky bit with SGID
[edit]3171 on a directory named 'blog' owned by the group 'engineers' and the user 'root'
A user named 'torvalds' who belongs primarily to the group 'torvalds' but secondarily to the group 'engineers' creates a file or directory named 'thoughts' inside the directory 'blog'. A user named 'wozniak' who also belongs to the group 'engineers' cannot delete, rename, or move the file or directory named 'thoughts', because he is not the owner and the sticky bit is set. However, if 'thoughts' is a file, then 'wozniak' can edit it.
Sticky bit has the final decision. If sticky bit and SGID had not been set, the user 'wozniak' could rename, move, or delete the file named 'thoughts' because the directory named 'blog' allows read and write by group, and wozniak belongs to the group, and the default 0002 umask allows new files to be edited by group. Sticky bit and SGID could be combined with something such as a read-only umask or an append only attribute.
[ torvalds /home/shared/ ] $ groups torvalds
torvalds : torvalds engineers
[ torvalds /home/shared/ ] $ stat -c "%a %U:%G %n" ./blog/
3171 root:engineers ./blog/
[ torvalds /home/shared/ ] $ echo 'NEW FILE' > ./blog/thoughts
[ torvalds /home/shared/ ] $ su - wozniak
Password:
[ wozniak ~/ ] $ cd /home/shared/blog
[ wozniak /home/shared/blog/ ] $ groups wozniak
wozniak : wozniak engineers
[ wozniak /home/shared/blog/ ] $ stat -c "%a %U:%G %n" ./thoughts
664 torvalds:engineers ./thoughts
[ wozniak /home/shared/blog/ ] $ rm thoughts
rm: cannot remove ‘thoughts’: Operation not permitted
[ wozniak /home/shared/blog/ ] $ mv thoughts /home/wozniak/
mv: cannot move ‘thoughts’ to ‘/home/wozniak/thoughts’: Operation not permitted
[ wozniak /home/shared/blog/ ] $ mv thoughts pondering
mv: cannot move ‘thoughts’ to ‘pondering’: Operation not permitted
[ wozniak /home/shared/blog/ ] $ echo 'REWRITE!' > thoughts
[ wozniak /home/shared/blog/ ] $ cat thoughts
REWRITE!
Security
[edit]Developers design and implement programs that use this bit on executables carefully in order to avoid security vulnerabilities including buffer overruns and path injection. Successful buffer-overrun attacks on vulnerable applications allow the attacker to execute arbitrary code under the rights of the process exploited. In the event that a vulnerable process uses the setuid bit to run as root, the code will execute with root privileges, in effect giving the attacker root access to the system on which the vulnerable process is running.
Of particular importance in the case of a setuid process is the environment of the process. If the environment is not properly sanitized by a privileged process, its behavior can be changed by the unprivileged process that started it.[8] For example, GNU libc was at one point vulnerable to an exploit using setuid and an environment variable that allowed executing code from untrusted shared libraries.[9]
History
[edit]The setuid bit was invented by Dennis Ritchie[10] and included in su.[10] His employer, then Bell Telephone Laboratories, applied for a patent in 1972; the patent was granted in 1979 as patent number US 4135240 "Protection of data file contents". The patent was later placed in the public domain.[11]
See also
[edit]References
[edit]- ^ von Hagen, William (2010-05-13). Ubuntu Linux Bible. John Wiley & Sons. pp. 3–59. ISBN 9780470881804.
- ^ a b c Frisch, Æleen (2009-02-09). Essential system administration. O'Reilly. p. 351. ISBN 9780596550493.
- ^ Billimoria, Kaiwan N. (2018). Hands-On System Programming with Linux: Explore Linux system programming interfaces, theory, and practice. Packt Publishing Ltd. p. 250. ISBN 978-1-78899-674-7.
- ^ "Unix - Frequently Asked Questions".
- ^ "27.5 Directories and the Set-User-ID and Set-Group-ID Bits". GNU Coreutils 9.1. Free Software Foundation. Retrieved 13 December 2022.
- ^ "chmod -- change file modes". freebsd.org.
- ^ "open, openat -- open or create a file for reading, writing or executing". freebsd.org.
- ^ Brown, Neil (November 23, 2010). "Ghosts of Unix past, part 4: High-maintenance designs". LWN.net. Retrieved 30 March 2014.
- ^ Edge, Jake (October 27, 2010). "Two glibc vulnerabilities". LWN.net. Retrieved 30 March 2014.
- ^ a b McIlroy, M. Douglas (1987). A Research Unix reader: annotated excerpts from the Programmer's Manual, 1971–1986 (PDF) (Technical report). CSTR. Bell Labs. 139.
- ^ "Summary of key software patents".
External links
[edit]- Chen, Hao; Wagner, David; and Dean, Drew; Setuid Demystified (pdf)
- Tsafrir, Dan; Da Silva, Dilma; and Wagner, David; The Murky Issue of Changing Process Identity: Revising Setuid Demystified (pdf)
- Pollock, Wayne; Unix File and Directory Permissions and Modes
Setuid
View on Grokipedials command by an 's' in the owner's execute permission position (e.g., -rwsr-xr-x for a file owned by root).[2] For the bit to function, the file must also have execute permissions for the relevant users or groups; if execute is absent, the 's' appears as a capital 'S' but the setuid effect does not activate.[3] The effective user ID change applies only during execution and reverts afterward unless explicitly managed by the program using system calls like setuid().[1] This behavior is defined in POSIX standards and implemented consistently across systems like Linux, Solaris, and BSD variants, though some details (e.g., handling on non-executable files) may be implementation-defined.[1]
The setuid bit is set or cleared using the chmod command, either symbolically (e.g., chmod u+s filename to enable it for the user) or numerically by adding 4 to the octal permission mode (e.g., chmod 4755 filename for owner read/write/execute, group/other read/execute, plus setuid).[3] Only the file owner or a privileged process (typically root) can modify these special bits.[1] On directories, the setuid bit has no standard effect in POSIX and is typically ignored by implementations.[3]
Common uses of setuid include utilities that require temporary root privileges, such as /usr/bin/[passwd](/page/Passwd) (which updates shadow password files) or /usr/bin/[sudo](/page/Sudo) (which escalates privileges for authorized commands).[2] These programs are owned by root and have the setuid bit set, allowing ordinary users to perform administrative tasks securely.[4] However, setuid should only be applied to trusted, binary executables, as it does not work reliably on shell scripts due to security restrictions in most kernels.[3]
Despite its utility, the setuid bit poses significant security risks if misapplied, as it can enable privilege escalation vulnerabilities if the executable contains bugs or is replaced by malware.[2] System administrators are advised to audit setuid files regularly (e.g., using find / -perm -4000) and minimize their use, preferring alternatives like sudo or capabilities for finer-grained privilege control.[4] Historical exploits, such as those in misconfigured setuid programs, have led to widespread compromises, underscoring the need for careful implementation.[2]
Permission Bits in Unix-like Systems
Standard File Permissions
In Unix-like operating systems, file permissions follow a standard nine-bit model that controls access based on user categories and operation types. This model divides permissions into three categories—owner (the user who owns the file), group (users in the file's associated group), and other (all remaining users)—with each category assigned three bits representing read (r), write (w), and execute (x) permissions, respectively.[3] The read permission allows viewing file contents or listing directory contents, write enables modification or deletion, and execute permits running files as programs or accessing directory contents.[3] Permissions are commonly represented in symbolic notation, such asrw-r--r--, where the first group of three characters applies to the owner, the second to the group, and the third to others; dashes indicate absent permissions.[3] Equivalently, octal notation uses three digits (one per category) from 0 to 7, where each digit sums the binary values of the bits: 4 for read, 2 for write, and 1 for execute (e.g., 644 corresponds to rw-r--r--, granting read/write to the owner and read-only to group and others).[3]
The chmod command modifies these permissions, accepting either symbolic or octal modes to add, remove, or set bits for specified categories.[3] For instance, chmod 755 file sets owner permissions to read/write/execute (7) and group/others to read/execute (5), while chmod u+x file adds execute permission for the owner only.[3] This command operates on files and directories alike, though execute on directories controls traversal rather than execution.[3]
Default permissions for newly created files and directories are determined by the umask, a value that masks out specific bits from the system's initial modes.[5] Files start with mode 0666 (read/write for all categories, no execute), and directories with 0777 (read/write/execute for all); the umask subtracts its octal value bitwise to clear permissions (e.g., a common umask of 0022 yields 0644 for files and 0755 for directories by removing write for group/others on files and write for others on directories).[5] The [umask](/page/Umask) command sets or displays this value in the current shell, influencing all subsequent creations until changed.[5]
This foundational model provides the basis for more advanced permission mechanisms, such as special bits that extend privilege handling.[3]
Special Permission Bits
In Unix-like operating systems, special permission bits extend the standard read (r), write (w), and execute (x) permissions by enabling specific privilege escalations or restrictions during file access or execution. The setuid bit, formally known as the set-user-ID bit and defined by the S_ISUID flag with an octal value of 04000, allows an executable file to run with the effective user ID of its owner rather than the invoking user's ID.[6][7] This mechanism supports tasks requiring elevated privileges without granting them broadly to users.[7] The setuid bit is set using the chmod command in symbolic mode asu+s (e.g., chmod u+s file) or in octal mode by prefixing the standard permissions with 4 (e.g., chmod 4755 file).[3] When active, it modifies process execution so the effective user ID aligns with the file owner's ID, facilitating secure delegation of administrative functions.[7]
For context, the setgid bit (S_ISGID flag, octal 02000, symbolic g+s) operates analogously but for group privileges: when set on an executable, it sets the effective group ID to that of the file's group, enabling group-specific resource access; on directories, it enforces new files to inherit the directory's group ownership.[6][3] The sticky bit (S_ISVTX flag, octal 01000, symbolic t or o+t) applies primarily to directories, preventing users from deleting or renaming files unless they own the file or directory, or possess superuser privileges; this is commonly used in shared writable directories like /tmp.[6][3]
These bits appear distinctly in the long listing format of the ls command. For setuid or setgid, an 's' replaces the execute 'x' in the owner or group permission triplet if execute is also granted (e.g., -rwsr-xr-x for setuid with owner execute, or -rwxr-sr-x for setgid); if execute is absent, a capital 'S' indicates the bit alone (e.g., -rwSr-xr-x).[3] The sticky bit shows as a 't' in the other's execute position (e.g., drwxrwxrwt), or 'T' if execute is not set.[3]
| Permission Bit | Flag | Octal Value | Symbolic Notation | Effect on Executables | Effect on Directories |
|---|---|---|---|---|---|
| Setuid | S_ISUID | 04000 | u+s | Runs with file owner's effective UID | N/A |
| Setgid | S_ISGID | 02000 | g+s | Runs with file group's effective GID | New files inherit directory's group |
| Sticky | S_ISVTX | 01000 | o+t or t | Historically restricted paging (obsolete) | Restricts file deletion to owners |
Mechanics of Setuid
Execution on Files
When an executable file with the setuid bit set is invoked in Unix-like systems, the kernel, via the exec family of functions, creates a new process image where the effective user ID (EUID) is set to the user ID of the file's owner, while the real user ID (RUID) remains unchanged and matches that of the invoking process. The saved set-user-ID is also updated to the file owner's user ID, enabling the process to perform actions as if owned by the file's owner during its execution. This mechanism is defined in the POSIX standard and applies only if the file system does not have the ST_NOSUID flag set.[8] This privilege elevation allows a non-privileged user to temporarily acquire the owner's permissions. For instance, a standard user executing the/usr/bin/[passwd](/page/Passwd) program, which is owned by root and has the setuid bit enabled, gains root-level access solely for updating password files, without altering the caller's RUID.[4]
The scope of privileges depends on the file owner: in setuid-root scenarios, where the owner is the superuser (UID 0), the process obtains full administrative capabilities; in contrast, setuid-nonroot cases limit the process to the specific, potentially restricted privileges of the non-root owner, such as access to particular resources without system-wide control.[8]
Upon termination of the process—whether through normal exit or error—the elevated EUID is no longer active, as the process ceases to exist and cannot propagate its privileges to new processes unless explicitly forked beforehand.[8]
For security, setuid executables should be invoked using absolute paths (e.g., /usr/bin/passwd) rather than unqualified names resolved via the PATH environment variable, to avoid executing unintended malicious binaries that could exploit the setuid mechanism if placed in searchable directories.[9]
Behavior on Directories
In Unix-like systems, the setuid bit on directories is implementation-defined and often ignored, meaning it does not alter the behavior of file creation or ownership inheritance in most standard environments. According to the POSIX standard, whether requests to set or clear the set-user-ID-on-execution bit (S_ISUID, equivalent to mode 04000) on non-regular files such as directories are honored is left to the discretion of the implementation, allowing systems to disregard such operations without violating compliance.[1] In practice, this results in the bit having no functional effect on directories across many platforms, including Linux, where new files and subdirectories created within a setuid directory inherit the user ID (UID) of the creating process rather than the directory's owner.[10] However, on select systems like certain configurations of FreeBSD, the setuid bit can enforce UID inheritance when explicitly enabled. In FreeBSD, if the S_ISUID bit is set on a directory and the filesystem is mounted with the MNT_SUIDDIR option (typically on UFS filesystems), any new files or subdirectories created inside will adopt the directory's owner UID, overriding the creating user's UID; additionally, the execute bits are cleared on new files for security, and the bit is propagated to new subdirectories.[11] This mechanism is a non-standard extension designed primarily for specialized environments, such as fileservers running FTP or Samba, where maintaining consistent file ownership across multiple users is essential to prevent permission conflicts in shared workspaces— for instance, ensuring uploaded files in a public dropbox are owned by a service account rather than individual contributors.[11] The GNU Coreutils documentation notes that such behavior occurs on only a few systems, emphasizing its rarity and advising against reliance in portable scripts, as POSIX explicitly permits ignoring these bits on directories.[12] A key limitation of the setuid bit on directories is that it has no impact on execution privileges, as directories are not executable in the same manner as files; it solely influences ownership assignment during creation and does not grant elevated permissions to processes interacting with the directory.[12] It is frequently combined with the setgid bit to achieve fuller control over inheritance, allowing new files to match both the directory's UID and group ID (GID) for collaborative scenarios.[11] To check the setuid bit on a directory, thels -ld command displays it as an 's' in the owner's execute position (e.g., drwxr-sr-x), while setting it requires chmod u+s <directory> or numeric mode like chmod 4xxx <directory>, though these operations may succeed without effect on unsupported systems.[1]
Platform variations further complicate its use: Linux and most POSIX-compliant systems ignore the bit entirely for directories, treating it as a no-op during file creation.[10] In contrast, FreeBSD requires kernel configuration via the SUIDDIR option and is limited to local UFS filesystems, with no support on ZFS or other modern filesystems.[11] Networked filesystems like NFS typically do not honor setuid on directories due to security concerns over cross-machine UID mapping, rendering the bit ineffective in distributed environments such as Azure NetApp Files or standard NFSv3/v4 exports.[13]
Implementation Details
Process Credentials
In Unix-like operating systems, particularly Linux, process credentials are managed by the kernel through specific user and group identifier fields stored in thestruct cred structure, which is immutable once committed to a task except for reference counting and keyring modifications.[14] These credentials include the real user ID (RUID), which identifies the user owning the process and is preserved across fork() and execve() calls; the effective user ID (EUID), which determines the process's permissions for accessing resources like files; and the saved set-user-ID (SUID), which stores a previous EUID value for potential restoration during privilege changes.[15] Equivalent group identifiers exist for setgid functionality: the real group ID (RGID) for ownership, the effective group ID (EGID) for permission checks, and the saved set-group-ID (SGID) for group privilege switching.[15]
During non-setuid execution of a binary via the execve() system call, the process inherits credentials from its parent, with the RUID unchanged, the EUID unchanged, and the SUID set to the EUID; if the caller has EUID equal to RUID (typical for unprivileged processes), all three will match the caller's real user ID.[16] In contrast, when executing a setuid binary—where the file's set-user-ID bit is set—the kernel adjusts the credentials as follows: the EUID is set to the user ID of the file's owner to grant elevated privileges for the duration of execution, while the RUID remains the caller's ID to preserve the original ownership context; the SUID is then assigned the value of the new EUID (the file owner's ID), allowing the process to later switch privileges back using calls like setreuid() if needed.[16][17] The same logic applies to setgid binaries for group credentials: the EGID is set to the file's group ID, the RGID stays as the caller's, and the SGID captures the new EGID for potential reversal.[16] These adjustments occur only if certain conditions are met, such as the absence of the no_new_privs flag, a non-nosuid filesystem mount, and no active tracing via ptrace().[16]
The assignment of these credentials during execve() can be illustrated in pseudocode, reflecting the kernel's handling in the do_execve() path:
if (file_has_setuid_bit) {
new_euid = file_owner_uid;
new_suid = new_euid; // Save the elevated UID
// RUID unchanged: new_ruid = current_ruid
} else {
new_euid = current_euid;
new_suid = new_euid;
new_ruid = current_ruid;
}
// Commit new credentials to task's cred structure
task->cred->uid = {new_ruid, new_euid, new_suid};
if (file_has_setuid_bit) {
new_euid = file_owner_uid;
new_suid = new_euid; // Save the elevated UID
// RUID unchanged: new_ruid = current_ruid
} else {
new_euid = current_euid;
new_suid = new_euid;
new_ruid = current_ruid;
}
// Commit new credentials to task's cred structure
task->cred->uid = {new_ruid, new_euid, new_suid};
struct cred.[14][16] For group IDs, the process mirrors this, substituting GIDs in place of UIDs when the set-group-ID bit is present.[15]
System Calls Involved
Thechmod and fchmod system calls are used to set the setuid bit on files and directories. The chmod function changes the mode of a file specified by pathname, while fchmod operates on an open file descriptor; both allow setting the set-user-ID bit (S_ISUID, octal 04000) in the file mode bits, which enables the special permission for subsequent executions.[10] These calls require the calling process to have appropriate privileges, such as the effective user ID matching the file owner or possession of the CAP_FOWNER capability; otherwise, they return -1 with errno set to EPERM.[10] Note that on some filesystems, writing to the file without the CAP_FSETID capability may automatically clear the setuid bit as a security measure.[10]
The execve system call and its family (such as execl, execvp) trigger the setuid mechanism during program loading. When executing a file with the setuid bit set, execve changes the effective user ID (EUID) of the new process to the file owner's UID, provided certain conditions are met, such as the filesystem not being mounted with the nosuid option (MS_NOSUID flag).[16] The real user ID (RUID) remains unchanged unless the process is privileged, and the original EUID is copied to the saved set-user-ID for potential later use.[16] If the process is being ptraced, the no_new_privs prctl attribute is set, or the filesystem is nosuid, the setuid bit is ignored (no EUID change to file owner), but execve still succeeds in executing the program unless other permission restrictions apply.[16]
At runtime, the setuid, seteuid, and setreuid system calls allow processes—particularly those launched with setuid—to modify user ID credentials, but only under specific conditions tied to saved set-user-ID privileges. The setuid call sets the effective UID and, if the process has the CAP_SETUID capability, also updates the real UID and saved set-user-ID; for non-privileged processes, it succeeds only if the specified UID matches the real or saved set-user-ID.[18] Similarly, seteuid permits temporary changes to the effective UID, enabling setuid-root programs to drop and regain privileges via the saved set-user-ID, while setreuid simultaneously sets both real and effective UIDs for more flexible management.[18] These calls return -1 with errno set to EPERM if the process lacks CAP_SETUID and the UID does not match the real or saved set-user-ID, preventing unauthorized privilege escalations.[18]
For verification purposes, the getuid and geteuid system calls retrieve the current user IDs without error conditions. getuid returns the real user ID of the calling process, while geteuid returns the effective user ID, allowing programs to inspect credential changes induced by setuid execution.[19] These queries always succeed and do not set errno, providing a reliable way to confirm the active privileges in process credentials.[19]
Practical Usage
Permission Verification
Permission verification for the setuid bit on files and directories in Unix-like systems can be performed using both command-line tools and programmatic interfaces, allowing administrators and developers to inspect whether the special permission is enabled. Thels -l command provides a straightforward way to visually inspect setuid permissions in the long listing format, where the owner execute permission position (the third character in the permission string) displays a lowercase 's' if the setuid bit is set and the execute bit is also enabled, or an uppercase 'S' if the setuid bit is set but the execute bit is not. For example, output like -rwsr-xr-x indicates setuid with owner execute privileges, while -rwSr-xr-x shows setuid without the execute bit. This notation applies similarly to directories, though setuid on directories typically has limited practical effect beyond inheritance in some filesystems. The distinction between 's' and 'S' helps identify not only the presence of the setuid bit but also the accompanying execute permission status.[20]
For more detailed numerical inspection, the stat command outputs the file mode in octal, where the setuid bit corresponds to the 4 in the leading digit (e.g., 4755 for a setuid executable with standard read/write/execute permissions). Running stat filename reveals the full mode bits, such as Access: (04755/-rwsr-xr-x), confirming the setuid presence through the 4xxx pattern. This tool is particularly useful for scripting or precise verification, as it provides raw mode values without symbolic interpretation.[21][22]
To locate all setuid-enabled files system-wide, a common Bash one-liner uses the find command: find / -perm -4000, which searches for files where the setuid bit is set (the -4000 specifies the bit in octal, with the minus indicating it must be present regardless of other permissions). This can be extended with -type f to limit to regular files or -exec ls -l {} \; to display details, aiding in security audits by listing potential privilege-escalation vectors. For directories, the same command identifies them if the bit is set, though such cases are rare and often ignored in practice.[23]
Programmatically, applications in C or similar languages can verify the setuid bit using the stat() or fstat() system call to retrieve the file's st_mode field, then checking if (st_mode & S_ISUID) != 0, where S_ISUID is the macro defined as 04000 in octal (S_IXUSR << 3). The access() function can also test execute permissions but does not directly reveal the setuid bit; thus, stat() is preferred for comprehensive checks. This approach, defined in POSIX standards, ensures portable detection across Unix-like systems.[6]
Common Setuid Programs
One prominent example of a setuid-root program is thepasswd command, which permits non-root users to modify their own password entries in the protected /etc/shadow file—a operation that necessitates root privileges for file access and integrity. By executing with the effective user ID set to root, passwd ensures that only the invoking user's record is altered, leveraging PAM modules for secure authentication and validation.[24][25]
The ping utility provides another standard case, traditionally implemented as setuid-root to enable the creation of raw IP sockets for sending ICMP echo requests and receiving responses, a low-level network operation restricted to privileged processes to mitigate potential denial-of-service risks. This allows diagnostic tools like ping to be accessible system-wide without requiring users to switch to root. Many contemporary Linux distributions supplement or replace this with the CAP_NET_RAW capability for finer-grained privilege control, but the setuid approach remains in legacy or minimal configurations.[24][26]
Sudo, the sudo command, operates as a setuid-root binary to facilitate controlled privilege escalation, allowing authorized users to execute specified commands with root (or other user) privileges based on policies defined in /etc/sudoers. This mechanism supports administrative delegation while enforcing logging and authentication, avoiding the need for direct root logins.[24]
In certain Linux distributions, the mount command is set as setuid-root to permit non-root users to attach filesystems designated with the user option in /etc/fstab, such as floppies or USB drives, thereby promoting usability for removable media without full administrative access. The user mount option explicitly relies on this setuid configuration in the mount binary to override the kernel's default nouser restriction.[27]
To demonstrate setuid behavior in code, the following simple C program prints the real and effective user IDs upon execution:
#include <stdio.h>
#include <unistd.h>
int main(void) {
[printf](/page/Printf)("Real UID: %d\n", getuid());
[printf](/page/Printf)("Effective UID: %d\n", geteuid());
return 0;
}
#include <stdio.h>
#include <unistd.h>
int main(void) {
[printf](/page/Printf)("Real UID: %d\n", getuid());
[printf](/page/Printf)("Effective UID: %d\n", geteuid());
return 0;
}
chmod u+s), running it as a non-privileged user will display the caller's real UID alongside an effective UID of 0 (root), illustrating the inheritance of the file owner's credentials for the process.
Security Considerations
Potential Risks
The setuid mechanism enables programs to execute with the privileges of their owner, often root, which introduces significant risks of privilege escalation if vulnerabilities are present in those programs. Attackers can exploit bugs, such as buffer overflows or input validation errors, to overwrite memory and redirect execution to malicious code, thereby gaining elevated privileges like root access. This occurs because the effective user ID (EUID) of the process temporarily matches the file owner's ID during execution, allowing untrusted code to perform actions beyond the caller's permissions.[28] Historical exploits frequently targeted setuid-root binaries through buffer overflows, enabling local users to escalate privileges. For instance, in IBM AIX 4.2 and earlier, the ping utility contained a buffer overflow vulnerability that allowed local attackers to gain root privileges by providing a long command-line argument, as the program processed input without adequate bounds checking while running with setuid-root permissions. Similarly, vulnerabilities in mount and umount utilities in various Unix-like systems, such as buffer overflows from long relative paths, permitted local privilege escalation by exploiting the elevated context needed for filesystem operations. These incidents highlight how seemingly innocuous utilities, when setuid, become prime targets for memory corruption attacks that lead to full system compromise.[29][30] More recent examples include CVE-2025-23395 in the Screen 5.0.0 utility (as of May 2025), where setuid-root execution in the logfile_reopen() function fails to drop privileges while operating on user-controlled files, enabling local attackers to gain root access.[31] Race conditions, particularly time-of-check-to-time-of-use (TOCTOU) issues, pose another threat when setuid programs interact with files. In such scenarios, a program checks a file's permissions or attributes at one point but uses them later after a potential change, allowing attackers to manipulate the file between the check and use phases. For example, in the Linux kernel's execve() implementation, a TOCTOU vulnerability enabled unauthorized execution of setuid files by altering permissions (e.g., adding setuid bits) between the initial open and the privilege application, potentially leading to unintended root access during package updates or file operations. This type of flaw exploits the non-atomic nature of filesystem interactions in privileged contexts.[32] Overuse of the setuid bit expands the attack surface unnecessarily, as each setuid program represents a potential entry point for exploitation if not rigorously audited. When developers or administrators setuid more binaries than required—violating the principle of least privilege—they increase the number of components running with elevated rights, amplifying the impact of any single vulnerability. Studies of setuid-to-root binaries have identified dozens of privilege escalation flaws stemming from this practice, underscoring how broader deployment heightens systemic risks without corresponding benefits.[30] In modern containerized environments like Docker, setuid binaries can bypass isolation mechanisms, enabling privilege escalation from within a container to the host system. Containers often inherit host kernel capabilities, and a vulnerable setuid program inside one can execute with host-level privileges if not restricted, such as through user namespaces or capability drops. This threat is exacerbated in multi-tenant setups, where compromised containers might leverage setuid to escape boundaries and affect the broader infrastructure.[33]Mitigation Strategies
To mitigate the security risks associated with setuid programs, administrators should adhere to the principle of least privilege by avoiding setuid-root binaries whenever possible and instead granting only the specific capabilities required for functionality.[26] Linux capabilities divide root privileges into fine-grained units, allowing programs to perform necessary operations without full superuser access.[26] For instance, the ping utility traditionally required setuid-root to create raw sockets for ICMP echo requests, but modern distributions configure it with the CAP_NET_RAW capability using the setcap command (e.g.,setcap cap_net_raw+ep /bin/ping), limiting its scope to network raw access and reducing the attack surface if exploited.[34][26]
Auditing setuid executions is essential for detecting unauthorized or anomalous usage. The auditd daemon, part of the Linux Audit Framework, can monitor process executions by adding rules via auditctl to track the execve system call where the saved user ID differs from the real user ID (e.g., auditctl -a always,exit -F arch=b64 -S execve -F suid!=0).[35] This generates logs for setuid transitions, enabling forensic analysis of privilege escalations.[35]
Where setuid is unavoidable, alternatives such as wrappers, service managers, or mandatory access controls provide safer mechanisms for privilege management. Setuid wrappers are small privileged binaries that invoke unprivileged scripts or programs after validating inputs, preventing direct exposure of elevated privileges to interpreters like shells.[36] Systemd services offer a structured alternative by running processes under specific users or with dynamic users (via DynamicUser=yes in unit files), avoiding setuid bits altogether while integrating with privilege separation. Additionally, confinement tools like AppArmor and SELinux enforce mandatory access controls on setuid programs, restricting their file access, network capabilities, and system calls even after privilege escalation. For example, AppArmor profiles can limit a setuid binary to read-only access on specific paths, while SELinux policies define type enforcement to prevent unauthorized transitions.
Regular audits and maintenance guidelines help minimize unnecessary setuid exposure. Administrators can identify setuid and setgid binaries using the find command (e.g., find / -xdev $ -perm -4000 -o -perm -2000 $ to search across the root filesystem), reviewing results to disable bits on non-essential files with chmod u-s or chmod g-s.[37] Package managers like dpkg or rpm can confirm if binaries belong to installed software, aiding decisions to remove or replace them.[37]
To further restrict setuid execution, non-root filesystems should be mounted with the nosuid option, which prevents the kernel from honoring set-user-ID, set-group-ID bits, or file capabilities on executables within that mount.[38] This is particularly useful for user-writable partitions like /home or /tmp, as specified in /etc/fstab (e.g., UUID=xxx /home [ext4](/page/Ext4) defaults,nosuid,nodev,noexec 0 2), blocking potential privilege escalations from untrusted storage.[38][27]
Historical Context
Early Development
The setuid mechanism originated at Bell Laboratories in the early 1970s, where it was developed as part of the foundational work on the Unix operating system to provide controlled privilege escalation for multi-user environments. One early motivation was to allow multiplayer games to update shared high-score files securely, without granting full root access to users. Invented by Dennis Ritchie, a key member of the Unix team alongside Ken Thompson, the feature addressed the need for secure sharing of administrative capabilities without exposing full superuser access.[39] The primary motivation for setuid was to enable ordinary users to run specific programs with elevated permissions belonging to the file's owner, thereby allowing tasks like updating system files while minimizing security risks associated with unrestricted root privileges. This design facilitated privilege sharing in a controlled manner, such as permitting non-root users to modify password entries without granting broader system control.[39] Bell Laboratories filed a patent application for the setuid bit in 1973, which was granted on January 16, 1979, as U.S. Patent 4,135,240, titled "Protection of Data File Contents."[39] The initial implementation appeared in early Research Unix releases, with its first notable application in commands likepasswd, which required temporary root privileges to update protected files such as /etc/passwd.
Early documentation of the associated setuid system call is found in the Version 7 Unix Programmer's Manual, released by Bell Labs in 1979, where it is described under section 2 as a means to change the effective user ID of a process. This manual entry marked a key point of formalization, reflecting the mechanism's integration into the core Unix kernel by that time.
