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!

成功了哈,我們成功的控制了程序的驗證分支,雖然只是一個很簡單的例子,我也是個菜鳥,希望可以對初學的同學有幫助,大家一起

交流 :)

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章