Lesson 16 / 20
Special Permission Bits
The three bits above the nine permission bits; setuid and setgid, which change who a program runs as, group inheritance in directories, and the sticky bit that narrows delete permission.
Contents
The users lesson left one detail open: in the permission string of the password-change
program, there was an s instead of an x at the third position of the owner class.
$ ls -l /usr/bin/passwd -rwsr-xr-x 1 root root 72056 May 30 2024 /usr/bin/passwd
This lesson explains that letter and its two siblings. Three extra bits solve three separate problems the nine permission bits cannot, and all three are forms of privilege escalation — this time embedded in the file’s own metadata.
Four-Digit Notation
The permission field is actually twelve bits, not nine. Three extra bits form the fourth digit from the left in octal notation:
| Bit | Octal | Name |
|---|---|---|
| setuid | 4000 | Set user ID |
| setgid | 2000 | Set group ID |
| sticky | 1000 | Sticky |
The full value can be read with stat:
$ stat -c '%a %A %n' scripts/summary.sh share 4755 -rwsr-xr-x scripts/summary.sh 3770 drwxrws--T share
4755 means setuid plus 755; 3770 means setgid plus sticky plus 770. chmod can take a
four-digit value; the symbolic counterparts are u+s, g+s, and +t.
The extra bits do not get their own column; they change the letter at the existing x
positions:
| Position | With x on |
With x off |
|---|---|---|
| Owner (setuid) | s |
S |
| Group (setgid) | s |
S |
| Others (sticky) | t |
T |
An uppercase letter reports that the extra bit is on but the execute bit beneath it is off.
This is usually a sign of misconfiguration; the drwxrws--T output above, though, is an
intended state — the “others” class has been given no permission at all on this directory.
setuid: Changing Who the Runner Is
Normally a program runs with the identity of the user who runs it. If the setuid bit is on for an executable file, it runs with its owner’s identity, not the runner’s.
The password-change program is marked this way for exactly this reason. The program is owned by root and has the setuid bit on; when an ordinary user runs it, the process runs with root privilege and can update the password file, which only root can write to. The user does not access the file directly; they access a controlled program that does.
The model follows the same principle as the policy in the privilege escalation lesson, but keeps the privilege not in an externally defined policy but in the file’s own metadata. Two consequences follow.
setuid programs are an attack surface. Every bug in such a program can turn into privilege
gain. For this reason setuid programs are kept few, do narrow work, and scrutinize their
input closely. The list of setuid files on a system can be pulled with find; the criterion
option carries the name -perm and is a standard step in routine audits.
It is ignored in shell scripts. The kernel does not apply this bit to files run through an interpreter:
$ printf '#!/bin/sh\nid -un\n' > /tmp/identity.sh $ chmod 4755 /tmp/identity.sh $ ls -l /tmp/identity.sh -rwsr-xr-x 1 student student 17 Jul 26 19:31 /tmp/identity.sh
The result does not change when another user runs it:
$ id -un backup $ /tmp/identity.sh backup
Although the bit appears on, the process ran with the backup identity. The reason is a race
condition: in the time between the kernel opening the file and running the interpreter, the
file could be changed, and the privileged interpreter could end up running a different script.
This door is closed by ignoring the bit.
setgid: On Files and On Directories
On executable files, the setgid bit is setuid’s group counterpart: the process runs with the file’s group, not the runner’s.
On directories it carries an entirely different and far more commonly used meaning: new files created in the directory take the directory’s group, not the creator’s primary group.
This was exactly the problem left open in the ownership lesson. The tree’s group had been changed, but new files still formed with the user’s primary group.
$ mkdir -p share $ chgrp project share $ chmod 770 share $ ls -ld share drwxrwx--- 2 student project 4096 Jul 26 19:30 share $ touch share/test-1.csv $ ls -l share total 0 -rw-r--r-- 1 student student 0 Jul 26 19:30 test-1.csv
Even though the directory belongs to the project group, the file created inside it takes on
the student group; group members cannot write to it. Once the setgid bit is added, the
behavior changes:
$ chmod g+s share $ ls -ld share drwxrws--- 2 student project 4096 Jul 26 19:30 share $ touch share/test-2.csv $ ls -l share total 0 -rw-r--r-- 1 student student 0 Jul 26 19:30 test-1.csv -rw-r--r-- 1 student project 0 Jul 26 19:30 test-2.csv
The file created before the bit kept its old group; the one created after took the directory’s group. The bit is also copied to newly created subdirectories, so the inheritance persists across the whole tree.
For group sharing to work, three settings need to be made together: the directory’s group, group write permission, and the setgid bit. If any one of the three is missing, sharing does not work, and the symptom looks different each time.
For new files to also get group write permission, the mask needs adjusting too; the 022 mask
from the permission bits lesson turns off group write. When working in shared directories, the
mask is set to 002.
The Path Must Be Passable
Sharing does not end with directory permissions. The rule from the path resolution lesson comes into play here: reaching a shared directory requires pass-through permission at every directory along the path.
$ chgrp project ~ $ chmod 710 ~ $ ls -ld ~ drwx--x--- 5 student project 4096 Jul 26 19:13 /home/student
A value of 710 gives the owner full access and the group only pass-through permission. Group
members cannot list the home directory’s contents, but can pass through it:
$ id -un backup $ ls /home/student ls: cannot open directory '/home/student': Permission denied $ ls /home/student/project/share test-1.csv test-2.csv
The reverse of the “read without pass-through” example from the permission bits lesson has become a real configuration here: the home directory’s contents stay hidden while a subdirectory whose name is known can be shared.
The Sticky Bit
The shared directory now works, but the gap left open in the permission bits lesson is still open: a user with write permission on the directory can delete any file inside it.
$ id -un backup $ ls -l total 0 -rw-rw-r-- 1 student project 0 Jul 26 19:31 test-1.csv -rw-rw-r-- 1 student project 0 Jul 26 19:31 test-2.csv $ rm test-1.csv $ ls -l total 0 -rw-rw-r-- 1 student project 0 Jul 26 19:31 test-2.csv
The backup user deleted a file they did not own. The definition from the deletion lesson
explains this: deleting removes a directory entry, and the permission checked is the
directory’s write permission.
The sticky bit narrows this rule. In a directory with the bit on, only three parties can remove an entry: the file’s owner, the directory’s owner, and root.
$ chmod +t share $ ls -ld share drwxrws--T 2 student project 4096 Jul 26 19:31 share
The same user tries the same operation again:
$ rm test-2.csv rm: cannot remove 'test-2.csv': Operation not permitted $ touch backup-note.csv $ ls -l total 0 -rw-rw-r-- 1 student project 0 Jul 26 19:31 test-2.csv -rw-r--r-- 1 backup project 0 Jul 26 19:31 backup-note.csv $ rm backup-note.csv $ ls -l total 0 -rw-rw-r-- 1 student project 0 Jul 26 19:31 test-2.csv
Deleting someone else’s file is blocked; deleting a file the user created is not blocked. Write permission has not disappeared either — new files can still be added to the directory. The bit narrows only deletion and renaming.
This is the standard configuration for temporary directories on a system:
$ ls -ld /tmp drwxrwxrwt 1 root root 4096 Jul 26 19:24 /tmp
The rwxrwxrwt string says: everyone can write, but everyone can only delete their own file.
Without the sticky bit, the temporary directory would be a space where everyone could delete
everyone else’s files.
Summary
- The permission field is twelve bits; three extra bits form the fourth octal digit.
- The extra bits do not get their own column, they change the letter at the
xpositions; an uppercase letter reports the execute bit beneath it is off. - A setuid-marked executable runs with its owner’s identity, not the runner’s; it is ignored in shell scripts because of a race condition.
- In directories, setgid makes new files take the directory’s group and is copied to subdirectories; group sharing needs the group, write permission, and this bit together.
- Reaching a shared directory requires pass-through permission at every directory along the
path; a value of
710grants pass-through without listing. - The sticky bit lets only the file’s owner, the directory’s owner, and root remove an entry in a directory; it is the standard configuration for temporary directories.
Next Step
The identity, permission, and privilege model is complete; the project tree can now be safely shared with a group. One last layer about the system itself remains: how did the tools used to work in this tree get onto the system, how are they updated, and how are they removed? The next topic opens with the concept of a package manager and closes the course with the text editors needed to work in the tree.
To keep your progress and take notes, Log in
My notes
Log in to take notes.