setuid, setgid, and sticky bit in Linux and Unix (macOS)

Sunday, July 21, 2024 at 9:59 AM | 11 min read

Last modified on Monday, June 8, 2026 at 11:10 PM

#chmod, #file permissions, #linux, #linux mint, #unix, #macOS, #principle of least privilege, #special permissions, #setuid, #setgid, #sticky bit

Unique magenta tulip in a sea of tulip buds

Photo by João Jesus on pexels.com

Table of Contents

In previous posts I discussed changing file and directory permissions. In this post, I discuss elevating current user privileges without the use of sudo, and restricting privileges to root or current user regarding modifying or deleting files at the directory level.

setuid and sudo

The setuid (aka suid) file permission permits a user to execute a file or program with the permission of the owner of that file or program. In other words, setuid allows a user to run a program as the owner of the program file rather than as themselves.

One of the most popular setuid permissions is sudo. if I run which sudo in Terminal, it returns the following:

/usr/bin/sudo

The which command locates the paths of executable files such as sudo.

If I run ls -l /usr/bin/sudo, it returns:

-rwsr-xr-x 1 root root 232416 Apr 3 2023 /usr/bin/sudo

The sudo executable contains an "s" in its permissions instead of an "x" (execute permission). The "s" represents the setuid file permission. The rest of the world (groups and other) has execute (x) permissions for sudo. This means that when a user executes sudo, the OS will not execute the file as the standard user but as root. Therefore, sudo permits a standard user to perform elevated system functions without having to log in as the root user.

It is important to note, however, that only standard users with access to sudo can change ownership of files and directories. Any user that a standard user with sudo privileges creates on the system does not have access to sudo by default. At least this is the case in the Linux distribution I am using: Linux Mint.

How to set up setuid using chmod

Let's revisit the "chown-directory" I created in my post entitled The chown command in Linux and Unix (macOS) and its contents. I transferred ownership from user maria to user magdala for both the parent directory, "chown-directory", and all its files and subdirectory(s). In order to be able to modify a file, let's say chown-file1.txt without root privileges, I could run the following in Terminal using chmod in symbolic mode:

sudo chmod u+s chown-file1.txt

Here I am changing file permissions in symbolic mode. I can use chmod with numeric notation as well.

And then to check the result of running the above, I run ls -l inside the "chown-directory", and the following is returned:

total 4 -rwSrw-r-- 1 magdala magdala 0 Jul 20 23:19 chown-file1.txt -rw-rw-r-- 1 magdala magdala 0 Jul 20 23:19 chown-file2.txt srwxrwxr-x 2 magdala magdala 4096 Jul 20 23:19 chown-subdirectory

There is something a bit off here with the "S" that resulted from execution of sudo chmod u+s chown-file1.txt setting setuid on chown-file1.txt. It is uppercase instead of lowercase. This means that a standard user does not have execute permission to modify or delete chown-file1.txt. If I try and modify the file in Vim, and then save changes made to it, I get the following message:

E45: 'readonly' option is set (add ! to override)

When I add ! right after :x (:x!), I still cannot save the changes I made to the file. To get out of Vim I have to type :q!, which quits me out of Vim, and modifications are not saved.

In order to be able to execute chown-file1.txt, I have to add execute permissions using chmod:

sudo chmod -v u+x chown-file1.txt

which results in the following as stdout in Terminal:

[sudo] password for maria: mode of 'chown-file1.txt' changed from 4664 (rwSrw-r--) to 4764 (rwsrw-r--)

Now the "s" representing setuid is lowercase, just as with setuid permission for sudo. This means that now standard user maria has execute permissions for chown-file1.txt, owned by standard user magdala.

Technically, however, contrary to what is often shown in examples elsewhere, setuid would not work on a regular .txt file. It only works on executables, indicating that when running the executable, setuid will set its (execute) permission to that of the user (owner) who created it and not the user who executed it. This would be the root user instead of the standard user executing it.

rm: remove write-protected regular file 'chown-file1.txt'? y # I typed y for yes rm: cannot remove 'chown-file1.txt': Permission denied

Now if I try and actually modify chown-file1.txt, I still can't. The above examples are for demonstration only. Being able to modify or delete another user's file would create a grave security vulnerability to say the least. In my Linux Mint distribution, I can find out which commands have setuid set to them. This then would indicate whether standard user(s) I create with my standard user account with access to sudo have access to those commands (executables).

For example, if I run which su (switch user), which returns:

/usr/bin/su

And then run ls -l /usr/bin/su to find out what its permissions are, the following is returned:

-rwsr-xr-x 1 root root 55672 Feb 29 2022 /usr/bin/su

There is a lowercase s representing setuid instead of an x representing execute permission. This means that user magdala without access to sudo only has elevated access with the su command.

If I run which ls which returns:

/usr/bin/ls

And then run ls -l /usr/bin/ls to find out what its permissions are, the following is returned:

-rwxr-xr-x 1 root root 138208 Feb 7 2022 /usr/bin/ls

Here, setuid is not set to the ls command. Therefore, user magdala cannot use this command on files or directories that are owned by other users.

If I run which cat which returns:

/usr/bin/cat

And then run ls -l /usr/bin/cat to find out what its permissions are, the following is returned:

-rwxr-xr-x 1 root root 35280 Feb 7 2022 /usr/bin/cat

And so on. So if you are logged in as a user with limited access (cannot access sudo) and don't have access to an executable because of a file's or directory's ownership or permissions, you can take the following steps to find out what permissions have been set to that executable.

Another popular executable is passwd. The following are its permissions:

-rwsr-xr-x 1 root root 59976 Nov 24 2022 /usr/bin/passwd

This is a good thing. This means that standard users cannot modify or delete regular files or directories of other standard users. That's why it is important to limit user access to sudo on a local network where there are a number of standard users, because otherwise anyone would be able to tinker with anyone else's files on the system! In essence, the setuid bit has no effect on non-executable files of standard users, like .txt, for example.

Now let's use an executable file as an example. Let's say I create an executable called hello.sh. It contains the following content:

#!/bin/bash hello_world="Hello world!" echo $hello_world

setuid a shell script

Even though I may set the setuid for a shell script owned by another user, i.e., hello.sh:

-rwsrw-r-- magdala magdala 118 Jul 22 06:40 hello.sh

I cannot execute this script, even though I have set hello.sh to user execute permissions for setuid as indicated by the lowercase "s".

bash: ./hello.sh: Permission denied

I can't execute hello.sh using sudo because it returns an error:

sudo ./hello.sh # which returns: [sudo] password for maria: sudo: unable to execute ./hello.sh: No such file or directory

I can execute hello.sh without using sudo by running the following in Terminal:

bash ./hello.sh

I am able to execute hello.sh (have it "interpreted" by the bash "interpreter") because I am passing hello.sh explicitly to the interpreter (bash). Having hello.sh interpreted is just another way of stating "executed".

setuid a directory

setuid has no effect on directories.

setgid and crontab

setgid (aka sgid) is similar to setuid. When a process is executed, it will run as the group that owns the file. An example of a file that uses setgid is crontab. I can get the path to crontab by running the following command in Terminal:

which crontab

which returns:

/usr/bin/crontab

And if I run ls -l /usr/bin/crontab in Terminal, the following is returned:

-rwxr-sr-x 1 root crontab 39568 Mar 23 2022 /usr/bin/crontab

Just as setuid is set to the sudo command by default, setgid is set to the crontab command by default as well. crontab (aka CRON TABLE) is a file which contains a schedule of cron entries that are scheduled to run at specified times. cron applies to modern Linux, Unix, and some historical Unix variants such as Solaris. It is a utility that allows tasks to be automatically run in the background at regular intervals by the cron daemon. setgid is set to crontab for security reasons. This is a popular use case for setgid.

Group crontab has a very special purpose, and the crontab binary is owned by the crontab group and has a setgid bit as indicated in the file permissions above.

cron and crontab are beyond the scope of this post, but I will discuss it in a future one.

How to set up setgid using chmod

In order to setup setgid, I use the chmod command. This is similar to setting up setuid. For example:

# first I set setgid to the setgid-file.txt chmod g+s setgid-file.txt # Next I run `ls -l setgid-file.txt to check permissions ls -l setgid-file.txt ## which returns: -rw-rwSr-- 1 maria maria 0 Jul 15:07 setgid-file.txt

Since an uppercase S is returned, I know that setgid-file.txt does not have execute permissions, which is necessary. So next, I run the following:

# First I set execute permissions on setgid for setgid-file.txt chmod g+x setgid-file.txt # Next I run `ls -l setgid-file.txt to check permissions ls -l setgid-file.txt # which returns: -rw-rwsr-- 1 maria maria 0 Jul 22 15:07 setgid-file.txt

So why setuid or setgid?

I might want to use setuid or setgid if I wanted certain users or groups to be able to perform specific tasks that require root or superuser privileges, but I don't want to provide them with access to sudo. This is analogous to the principle of least privilege, where a user is given minimum levels of access or permissions needed to perform his/her job. It is important to note again that this relates to executable files (executables). One use of setuid is the passwd command, which uses it to allow users like myself to change passwords — an operation that requires root privileges.

Again, the setgid example above is for demonstration purposes only. Executables are set to setuid and setgid by default in the Linux OS, in my case, Linux Mint.

setgid and directories

Setting setgid on a directory results in different behavior than with files. When setgid is set to a directory, it causes all files that are created in that directory to be owned by the group of the directory and not the owner of the group.

Let's say I create a directory called "setgid-directory", and then I change the group of the directory using the chgrp command:

sudo chgrp magdala setgid-directory

which returns the following when I run ls -ld setgid-directory:

drwxrwxr-x 2 maria magdala 4096 Jul 22 16:54 setgid-directory

Then I set setgid to setgid-directory:

chmod g+s setgid-directory/

which returns the following in Terminal when I run ls -ld setgid-directory/:

drwxrwsr-x 2 maria magdala 4096 Jul 22 16:54 setgid-directory

Next, to test this out, I create a file inside the "setgid-directory" directory:

# create new file inside setgid-directory called setgid-file1.txt touch setgid-directory/setgid-file1.txt # check the group ownership of setgid-file1.txt ls -l setgid-directory # which returns the following: -rw-rw-r-- 1 maria magdala 0 Jul 22 16:54 setgid-file1.txt

The file I create inside "setgid-directory" inherits the group that "setgid-directory" belongs to.

So what is sticky bit?

The special permission sticky bit makes sure that only the owner of a file(s) inside a directory can delete or modify that file. A typical use of this is the /tmp/ directory. The /tmp/ directory can be written (w) by any user, but other users can't delete other users' files.

If I run ls -ld /tmp, the following is returned in Terminal:

drwxrwxrwt 16 root root 4096 Jul 22 16:35 /tmp

You can tell when sticky bit has been set on a directory when there is a t at the end of the permissions, as above.

Now let's test this out.

First I create a new directory called stickybit-directory.

Then I run the following command in Terminal:

chmod +t stickybit-directory

Next, I run ls -ld stickybit-directory and the following is returned:

drwxrwxr-t maria maria 4096 Jul 22 19:33 stickybit-directory

Then I create a file inside stickybit-directory called stickybit-file1.txt.

Next, I switch out of maria user with the su magdala command (su stands for switch user) and try to delete owner maria's stickybit-directory/stickybit-file1.txt:

su magdala Password: # maria user switches into magdala user magdala@maria-VirtualBox:/home/maria/Desktop$ # delete maria user's stickybit-directory/stickybit-file1.txt as magdala user rm stickybit-directory/stickybit-file1.txt # which returns: rm: remove write-protected regular empty file 'stickybit-directory/stickybit-file1.txt'? y # I type the "y" for "yes" # which returns: rm: cannot remove 'stickybit-directory/stickybit-file1.txt': Permission denied

Setting special permissions (setuid, setgid, and sticky bit) in numeric mode

Not only can I set special permissions in symbolic mode, but I can also set them in numeric mode.

Let's say I create a file called setuid-numeric-mode-file.txt and then set file permissions using chmod:

chmod 764 setuid-numeric-mode-file.txt

And running ls -l setuid-numeric-mode-file.txt returns the following:

-rwxrw-r-- 1 maria maria 0 Jul 22 20:33 setuid-numeric-mode-file.txt

7 is represented by rwx, 6 is represented by rw-, and 4 is represented by r--. To set setuid to setuid-numeric-mode-file.txt, I run the following in Terminal:

chmod 4764 setuid-numeric-mode-file.txt

4 represents setuid in numeric mode.

And when I run the ls -l setuid-numeric-mode-file.txt command, it returns the following:

-rwsrw-r-- 1 mariacam staff 0 Jun 8 16:24 setuid-numeric-mode-file.txt

The lowercase "s" represents the setuid.

Now let's say I create a file called setgid-numeric-mode-file.txt and then set file permissions using chmod:

chmod 2764 setgid-numeric-mode-file.txt

And running ls -l setgid-numeric-mode-file.txt returns the following:

-rwxrwSr-- 1 maria maria 0 Jul 22 20:44 setgid-numeric-mode-file.txt

And since the S is uppercase, I know that the file does not have execute permissions, so I also have to run the following to make that happen:

chmod 2774 setgid-numeric-mode-file.txt

Then I run ls -l setgid-numeric-mode-file.txt again and the following is returned in Terminal:

-rwxrwsr-- 1 maria maria 0 Jul 22 20:44 setgid-numeric-mode-file.txt

Now let's say I create a file called stickybit-numeric-mode-file.txt and then set permissions using chmod:

chmod 1764 stickybit-numeric-mode-directory

And when I run ls -ld stickybit-numeric-mode-directory in Terminal, the following is returned:

drwxrw-r-T maria maria 0 Jul 22 20:54 stickybit-numeric-mode-directory

I know that stickybit does not have execute permissions because the T that represents it is uppercase. So I run the following command in Terminal to fix that:

chmod 1765 stickybit-numeric-mode-directory

This time when I run ls -ld stickybit-numeric-mode-directory the following is returned:

drwxrw-r-t 2 maria maria 4096 Jul 22 21:01 stickybit-numeric-mode-directory

Removing special permissions (setuid, setgid, and sticky bit) in numeric mode

If I want to remove special setuid permissions in numeric mode, I would do the following:

chmod 04764 setuid-numeric-mode-file.txt

It returns:

-rwsrw-r-- 1 mariacam staff 0 Jun 8 16:24 setuid-numeric-mode-file.txt

The chmod command fails to remove the setuid in numeric mode. However, if I run:

chmod 0644 setuid-numeric-mode-file.txt

I get back the following:

-rw-r--r-- 1 mariacam staff 0 Jun 8 16:24 setuid-numeric-mode-file.txt

The user has no execute permissions, but the setuid has been removed. In order to remove the setuid, I had to downgrade the user permissions from "7" to "6".

If I wanted to maintain execute permissions on the file for the user, I would have to do the following:

chmod u-s setuid-numeric-mode-file.txt

This returns:

-rwxrw-r-- 1 mariacam staff 0 Jun 8 16:24 setuid-numeric-mode-file.txt

The "s" is replaced by an "x", so this way the user maintains execute permissions.

If I want to remove special setgid permissions in numeric mode, I could not. I could only do it in symbolic mode. I would do the following:

chmod 0774 setgid-numeric-mode-file.txt

Which returns:

-rwxrwxr-- 1 mariacam staff 0 Jun 8 15:30 setgid-numeric-mode-file.txt

Note that the "s" is replaced by an "x". setgid has been removed.

If I want to remove special sticky bit permissions in numeric mode, I would do the following in Terminal:

chmod 0775 stickybit-numeric-mode-directory

Which returns:

-rwxrwxr-x 1 mariacam staff 0 Jun 8 15:36 stickybit-numeric-mode-directory

The "t" has been replaced by an "x". sticky bit has been removed.

Now let's try to delete a file with sticky bit permissions:

# hello.sh file permissions -rwxrwxr-T magdala magdala 0 Jul 23 11:34 hello.sh # maria user tries to delete hello-world.sh rm hello.sh # which returns: rm: remove write-protected regular empty file 'hello.sh'? y # what I type for "yes" rm: cannot remove 'hello.sh': Permission denied whoami # I check to make sure who I am logged in as is as myself and not user magdala maria

It is important to note that I would not have been able to delete this shell script file even if I did not set sticky bit to hello.sh, because it is owned by another user. Only the actual root user would be able to do that.

Conclusion

In both Linux and Unix systems, there are various reasons to use setuid, setgid, and sticky bits. In Linux, a common use case for setuid, for example, is the passwd program. The passwd program needs to be able to update the root-owned passwd file, where the users' passwords reside. By setting setuid on the passwd binary and making it owned by root, a normal user like myself can change my password without having full root access. I actually found that so much easier to execute than in present-day macOS. In macOS Tahoe, I can use setuid to execute programs with the permissions of the file's owner, usually to perform tasks that require elevated privileges without being granted permanent access. This helps bolster security by limiting risks associated with elevated access. It also eliminates the need for me to act as root via the sudo command or even as actual root. However, with the advent of System Integrity Protection, important parts of the OS are safeguarded from modification, even from root. No use cases even leap out at me in modern, strictly protected macOS. In Linux, on the other hand, I am provided with much more flexibility in managing file permissions, and I can even access the root user much more easily. setuid, setgid, and sticky bit allow me to execute files with elevated privileges, control group ownership for new files, and restrict file deletion in shared directories, enhancing both the functionality and security of the OS. What I was especially surprised by was the ease with which I could switch between a standard user and root, and how much control root had over the OS as opposed to in macOS.