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

Bookmarks