Linux下栈溢出的初步之控制程序执行流程

昨天看了最基本的Linux下的栈溢出,今天用一个实例来练习一下一个Crackme

 

serial.c

#include<stdlib.h>
#include<stdio.h>
#include<string.h>
int valid_serial(char * psz)
{
    size_t len=strlen(psz);
    unsigned total=0;
    size_t i;
    if(len<10)
        return 0;
    for(i=0;i<len;i++)
    {
        if((psz[i]<'0')||(psz[i]>'z'))
            return 0;
        total+=psz[i];
    }
    if(total%853==83)
        return 1;
    return 0;
}
int validate_serial()
{
    char serial[24];
    fscanf(stdin,"%s",serial);
    if(valid_serial(serial))
       return 1;
    else
       return 0;
}
int do_valid_stuff()
{
    printf("The serial number is valid!\n");
    exit(0);
}
int do_invalid_stuff()
{
    printf("Invaild serial number!\nExiting...\n");
    exit(1);
}
int main()
{
    if(validate_serial())
      do_valid_stuff();
     else
      do_invalid_stuff();
     return 0;
}

 

 输入正确的序列号可以通过验证,这里在使用fscanf()函数的时候直接将用户的输入保存在了serial[24]这个数组中而没有验证大小,所以存在溢出。

尝试用44个‘a’时程序崩溃

root@bt:~/Desktop/code# ./serial
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
Invaild serial number!
Exiting...
root@bt:~/Desktop/code# ./serial
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
Segmentation fault

现在用Gdb调试:

查看main函数:

(gdb) disas main
Dump of assembler code for function main:
0x08048551 <+0>: push %ebp
0x08048552 <+1>: mov %esp,%ebp
0x08048554 <+3>: sub $0x8,%esp
0x08048557 <+6>: and $0xfffffff0,%esp
0x0804855a <+9>: mov $0x0,%eax
0x0804855f <+14>: add $0xf,%eax
0x08048562 <+17>: add $0xf,%eax
0x08048565 <+20>: shr $0x4,%eax
0x08048568 <+23>: shl $0x4,%eax
0x0804856b <+26>: sub %eax,%esp
0x0804856d <+28>: call 0x80484cf <validate_serial>
0x08048572 <+33>: test %eax,%eax
0x08048574 <+35>: je 0x804857d <main+44>
0x08048576 <+37>: call 0x8048515 <do_valid_stuff>
0x0804857b <+42>: jmp 0x8048582 <main+49>
0x0804857d <+44>: call 0x8048533 <do_invalid_stuff>
0x08048582 <+49>: mov $0x0,%eax
0x08048587 <+54>: leave
0x08048588 <+55>: ret
End of assembler dump.

可以看到在0x0804856d处调用了validate_serial()函数,假如我们把返回地址覆盖成0x08048576 ,即让验证程序直接返回到通过验证do_valid_stuff()这个分支。

有了这个想法我们先找一下溢出点:

(gdb) disas validate_serial
Dump of assembler code for function validate_serial:
0x080484cf <+0>: push %ebp
0x080484d0 <+1>: mov %esp,%ebp
0x080484d2 <+3>: sub $0x48,%esp
0x080484d5 <+6>: lea -0x28(%ebp),%eax
0x080484d8 <+9>: mov %eax,0x8(%esp)
0x080484dc <+13>: movl $0x804864c,0x4(%esp)
0x080484e4 <+21>: mov 0x80497a4,%eax
0x080484e9 <+26>: mov %eax,(%esp)
0x080484ec <+29>: call 0x804834c <__isoc99_fscanf@plt>
0x080484f1 <+34>: lea -0x28(%ebp),%eax
0x080484f4 <+37>: mov %eax,(%esp)
0x080484f7 <+40>: call 0x804842c <valid_serial>
0x080484fc <+45>: test %eax,%eax
0x080484fe <+47>: je 0x8048509 <validate_serial+58>
0x08048500 <+49>: movl $0x1,-0x2c(%ebp)
0x08048507 <+56>: jmp 0x8048510 <validate_serial+65>
0x08048509 <+58>: movl $0x0,-0x2c(%ebp)
0x08048510 <+65>: mov -0x2c(%ebp),%eax
0x08048513 <+68>: leave
0x08048514 <+69>: ret
End of assembler dump.

给0x080484ec下断点,

(gdb) break *0x080484ec
Breakpoint 1 at 0x80484ec

运行程序:

(gdb) run
Starting program: /root/Desktop/code/serial

Breakpoint 1, 0x080484ec in validate_serial ()
(gdb) x/20x $esp
0xbffff4c0: 0xb7fc9440 0x0804864c 0xbffff4e0 0xb7fc8ff4
0xbffff4d0: 0xb7f77d19 0xb7ea22a5 0xbffff4e8 0xb7e899d5
0xbffff4e0: 0xb7fc8ff4 0x08049774 0xbffff4f8 0x08048338
0xbffff4f0: 0xb7ff1030 0x08049774 0xbffff528 0x080485b9
0xbffff500: 0xb7fc9324 0xb7fc8ff4 0xbffff528 0x08048572


可以看到现在加粗的地方就是我们要覆盖的返回地址,输入43个‘a’。

(gdb) continue
Continuing.
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

Breakpoint 2, 0x08048514 in validate_serial ()

查看堆栈:

(gdb) x/20x 0xbffff4c0
0xbffff4c0: 0xbffff4e0 0x0804864c 0xbffff4e0 0xb7fc8ff4
0xbffff4d0: 0xb7f77d19 0xb7ea22a5 0xbffff4e8 0x00000000
0xbffff4e0: 0x61616161 0x61616161 0x61616161 0x61616161
0xbffff4f0: 0x61616161 0x61616161 0x61616161 0x61616161
0xbffff500: 0x61616161 0x61616161 0x00616161 0x08048572


现在可以看到再继续输入将会覆盖返回地址。

我们bash shell的printf函数,利用管道把printf的输出重定向到溢出程序,攻击字符串:

“aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\x76\x85\x04\x08”

其中\x76\x85\x04\x08是do_valid_stuff函数的地址。

root@bt:~/Desktop/code# printf "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\x76\x85\x04\x08" | ./serial
The serial number is valid!

成功了哈,我们成功的控制了程序的验证分支,虽然只是一个很简单的例子,我也是个菜鸟,希望可以对初学的同学有帮助,大家一起

交流 :)

 

posted @ 2012-07-08 23:08  Lamboy  阅读(1579)  评论(0编辑  收藏  举报