2
votes

I read some articles about Stack Buffer Overflow, like this one, and learned how attackers can exploit a Stack Buffer-Overflow bug by overwriting function pointers. Then I wrote a small program to demonstrate an attack:

#include <stdio.h>
#include <string.h>

void fun1 ( char * input ) {
    char buffer[10];
    strcpy( buffer, input );
    printf( "In fun1, buffer= %s\n", buffer );
}

void fun2 ( void ) {
    printf ( "HELLO fun2!\n" );
}

int main ( int argc, char * argv[] )
{
    printf ( "Address of fun2: %p\n", fun2 );
    fun1( "abcdefghijklmnopqrstuv\x52\x84\x04\x08" );
    return 0;
}

The program was compiled with GCC 4.5.1, under Fedora 14 x86. Below is the output:

$ ./exp01

Address of fun2: 0x8048452

In fun1, buffer= abcdefghijklmnopqrstuvR�

HELLO fun2!

HELLO fun2!

We can see that fun2() was called successfully, but I don't know why it ran twice. Then I GDBed it (See Below). (I know only some basic instruction about GDB ╮( ̄▽ ̄)╭ )

I Googled some key words such as "__libc_csu_fini ()", but didn't find a clear way that can help me to understand the program's execution path. I know too little about compiler and the inner structure of a process, so I consider that I may have to find some books or articles that describe these stuff in detail. Any suggestion? Thank you!


GDB Record:

(gdb) list

7 printf( "In fun1, buffer= %s\n", buffer );

8 }

9

10 void fun2 ( void ) {

11 printf ( "HELLO fun2!\n" );

12 }

13

14 int main ( int argc, char * argv[] )

15 {

16 printf ( "Address of fun2: %p\n", fun2 );

(gdb)

17 fun1( "abcdefghijklmnopqrstuv\x52\x84\x04\x08" );

18 return 0;

19 }

(gdb) break 16

Breakpoint 1 at 0x804846f: file hello.c, line 16.

(gdb) run

Starting program: /home/yuliang/test/hello

Breakpoint 1, main (argc=1, argv=0xbffff394) at hello.c:16

16 printf ( "Address of fun2: %p\n", fun2 );

Missing separate debuginfos, use: debuginfo-install glibc-2.13-2.i686

(gdb) step

Address of fun2: 0x8048452

17 fun1( "abcdefghijklmnopqrstuv\x52\x84\x04\x08" );

(gdb)

fun1 (input=0x804859a "abcdefghijklmnopqrstuvR\204\004\b") at hello.c:6

6 strcpy( buffer, input );

(gdb)

7 printf( "In fun1, buffer= %s\n", buffer );

(gdb)

In fun1, buffer= abcdefghijklmnopqrstuvR�

8 }

(gdb)

fun2 () at hello.c:10

10 void fun2 ( void ) {

(gdb)

11 printf ( "HELLO fun2!\n" );

(gdb)

HELLO fun2!

12 }

(gdb)

0x08048500 in __libc_csu_fini ()

(gdb)

Single stepping until exit from function __libc_csu_fini,

which has no line number information.

fun2 () at hello.c:10

10 void fun2 ( void ) {

(gdb)

11 printf ( "HELLO fun2!\n" );

(gdb)

HELLO fun2!

12 }

(gdb)

Cannot access memory at address 0x76757477

(gdb)

Single stepping until exit from function __libc_csu_init,

which has no line number information.

0x009aae36 in __libc_start_main () from /lib/libc.so.6

(gdb)

Single stepping until exit from function __libc_start_main,

which has no line number information.

Program exited with code 0241.

(gdb)

1
When you run the program without the printf in fun1 will the program execute twice as well?Azrael3000
You need to use a machine code debugger to step through this stuff --- look for the gdb nexti, stepi and disas commands. A debugger in C mode will get very confused because it relies on the stack frames being valid to know what's being executed where, and of course they aren't any more, because you've just changed them.David Given
Thank you for your reply @Azrael3000 . I comment out the printf in fun2. It goes back into the printf in main(), and gets into a dead loop. But the address of fun2() is now 0x804843e. If I call fun1( "abcdefghijklmnopqrstuv\x3e\x84\x04\x08" ) instead of fun1( "abcdefghijklmnopqrstuv\x52\x84\x04\x08" ), fun2 still run twice.Yuliang
Very helpful information. I really need to learn more about gdb. Thanks @DavidGivenYuliang
I agree with David. The debugger will rely on having a valid stack pointer and unwinding the stack from there.Gargi Srinivas

1 Answers

0
votes

When running your program in gdb break shortly before the strcpy() and have a look at the stack-frame (that is where the save eip is stored). then run until shortly after the printf() and again have a look at the stored eip (command is info frame).

since you pass the address of function fun2() to fun1() it will overwrite the saved eip and as soon as return is called (implicit in your case) the next instruction will be executed (which is given by eip and in your case it is the address of fun2())

And don't forget to read Smashing the stack for fun and profit by aleph1