SUID, SGID, Sticky Bit
Introduction
Special permission like SUID, SGID, Sticky bit can be set in files or directories using chmod
command.
These permissions allow files and directories to work effectively in collaborative environment.
SUID
SUID (Set User Identifier) allows to run a file with the privilege of the owner of that file. The runner must have the privilege to execute that file but while executing the file runner’s privilege is elevated to the owner.
Example
Lets see an example, lets check the permission of passwd
command:
$ ls -l $(which passwd)
-rwsr-xr-x 1 root root 59640 Jan 25 2018 /usr/bin/passwd
Note passwd command’s owner is root and owner’s privilege rws, execute bit is s meaning SUID is set for this executable.
passwd
command can be run in two ways. root user can change other’s password like below:
$ su root
$ passwd <user>
Any non privileged user can change his own password using passwd
command like below:
$ passwd
In both cases /etc/passwd
file is updated with new password. Now if we check the permission of /etc/passwd
file:
$ ls -l /etc/passwd
-rw-r--r-- 1 root root 2450 Jan 27 15:59 /etc/passwd
We can see only root has the write permission in /etc/passwd
file. Then how can a non privileged user changes the
content of /etc/passwd
file using some command. Hence the SUID bit.
As SUID bit is set in passwd
command, when an non privileged user executes the command (user must have execute
permission) he/she run the passwd
command with the privilege of root. Then passwd
command decides what the
regular user can do. In this case can only change his own password.
You may be wondering, as passwd
command is running with all the privilege of root, what if it is doing something
evil. Moreover what if some closed source command needs SUID bit to be set. Luckily starting form Kernel version 2.2
Linux has a feature called capabilites which can assign only the permission a command needs to finish his job.
We can set SUID bit on a file using chmod
.
$ cat hello.sh
#!/bin/bash
echo "Hello World"
$ ls -l hello.sh
-rw-rw-r-- 1 john john 31 Jun 20 10:54 /home/john/hello.sh
$ chmod u+s hello.sh
$ ls -l hello.sh
-rwSrw-r-- 1 john john 31 Jun 20 10:54 /home/john/hello.sh
As hello.sh was not executable when adding SUID for owner using u+s, executable bit is now S (Uppercase s). Now lets make hello.sh executable.
$ chmod u+x hello.sh
$ ls -l hello.sh
-rwsrw-r-- 1 john john 31 Jun 20 10:54 /home/john/hello.sh
As hello.sh is executable now SUID is denoted by lowercase s. Now make this script executable for other users.
$ chmod o+x hello.sh
$ ls -l hello.sh
-rwsrw-r-x 1 john john 31 Jun 20 10:54 /home/john/hello.sh
Now any other user can run the script with the permission of owner of the script ie. john. But for security reason changing SUID bit doesn’t have any impact on any scripts. If hello.sh was a compiled binary. It would work like as expected. Lets see that in action.
I have written a simple C code which prints the User ID of runner along with Effective UID, the user whose permission is active.
// file: program.c
#include <stdio.h>
#include <unistd.h>
int main(int argc, char** argv) {
printf("Runner UID: %d\n", getuid());
printf("Effective UID: %d\n", geteuid());
return 0;
}
Lets compile the program using gcc and change the owner to root.
$ gcc -o program program.c
$ ls -l program
-rwxr-xr-x 1 john john 8392 Jun 20 11:30 /home/john/program
$ sudo chown root:root program
$ ls -l program
-rwxr-xr-x 1 root root 8392 Jun 20 11:30 /home/john/program
$ ./program
UID: 1000
Effective UID: 1000
$ id -un 1000
john
Now lets change the SUID bit and run again.
$ sudo chmod u+s program
$ ls -l program
-rwsr-xr-x 1 root root 8392 Jun 20 11:30 /home/john/program
$ ./program
UID: 1000
Effective UID: 0
$ id -un 0
root
SGID
SGID (Set Group Identifier) works both on files and directories. When added on file it works just like SUID but for group.
$ sudo chown john:staff gprog
$ chmod o+x gprog
$ chmod g+s gprog
$ ls -l gporg
-rwxr-sr-x 1 john staff 31 Jun 20 11:30 /home/john/gprog
As SGID is set for this executable, an user who is not part of staff group can run this imaginary program with the permission of staff group.
SGID bit can also be set on directory. Normally when an user creates a new file, that file’s group is set to the user’s current active group. But if SGID bit is set for parent directory then new file’s group is set to parent directory’s group.
$ mkdir reports
$ ls -ld reports
drwxr-xr-x 2 john john 4096 Jun 20 12:13 reports/
$ cd reports
$ touch file1
$ ls -l file1
-rw-r--r-- 1 john john 0 Jun 20 12:17 file1
Now let’s change the group of reports directory to staff and set GUID bit. Now files created inside this directory will automatically belong to staff group as it is the group of parent directory.
$ sudo chown john:staff reports
$ sudo chmod g+s reports
$ ls -ld reports
drwxr-xr-x 2 john staff 4096 Jun 20 12:13 reports/
$ cd reports
$ touch file2
$ ls -l file2
-rw-r--r-- 1 john staff 0 Jun 20 12:17 file2
It is very useful for collaborative environment. Otherwise if a user creates some file without switching group that file becomes inaccessible for other users.
Sticky bit
Stick bit is only meaningful when set on directory. This is also a feature for collaborative environment. If sticky bit is set on a directory, then only the owner can remove or rename file inside that directory, even when the other regular user has write access.
$ sudo mkdir /sticky
$ sudo chown root:staff /sticky
$ sudo chmod ugo+rwx /sticky
$ sudo chmod +t /sticky
# Sticky bit is denoted with 't' ('T' if not executable)
$ ls -ld /sticky
drwxrwxrwt 2 root staff 4096 Jun 20 12:51 /sticky/
$ cd /sticky
$ touch file1
$ ls -l file1
rw-rw-rw- 1 john john 0 Jun 20 12:55 file
$ su jane
$ cd /sticky
$ rm file
rm: cannot remove 'file': Operation not permitted
$ mv file file1
mv: cannot move 'file' to 'file2': Operation not permitted
$ echo "Can write but can't delete" > file
sticky folder has complete rwx for everybody, but jane cannot delete files of john as sticky bit is set.
Tools Version
- chmod (GNU coreutils) 8.28
- gcc (Ubuntu 7.3.0-27ubuntu1~18.04) 7.3.0