【问题标题】:Intentionally Vulnerable Test Program Not Behaving as Expected故意易受攻击的测试程序未按预期运行
【发布时间】:2014-02-25 06:01:49
【问题描述】:

我一直在使用strcpysprintgets 等故意使用易受攻击的 c 程序。这些在 linux 上运行时都按预期运行,但在我的 OS X 机器上发生了一些奇怪的事情。这是我写的程序:

#include <stdio.h>
int main(int argc, char **argv) {
    char buffer[64];
    strcpy(buffer, argv[1]);
    printf("buffer: %s\n", buffer);
    return 0;
}

我是这样运行的:

(gdb) run test
Starting program: /Users/****/test2 test
buffer: test
[Inferior 1 (process 5290) exited normally]
(gdb) run `python -c 'print "A"*64'`
Starting program: /Users/****/test2 `python -c 'print "A"*64'`
buffer: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
[Inferior 1 (process 5291) exited normally]
(gdb) run `python -c 'print "A"*70'`
Starting program: /Users/****/test2 `python -c 'print "A"*70'`
buffer: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
[Inferior 1 (process 5294) exited normally]
(gdb) run `python -c 'print "A"*80'`
Starting program: /Users/****/test2 `python -c 'print "A"*80'`
buffer: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
[Inferior 1 (process 5297) exited normally]
(gdb) run `python -c 'print "A"*100'`
Starting program: /Users/****/test2 `python -c 'print "A"*100'`
buffer: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

Program received signal SIGABRT, Aborted.
0x00007fff8eef3866 in ?? ()
(gdb) 

首先,我预计 80 字节足以让它崩溃。其次,我希望看到0x4141414141414141 而不是0x00007fff8eef3866,因为我只是试图用一堆A 覆盖一些内存。其他数据从何而来?另外,为什么该程序会获得 SIGABRT?为什么没有seg fault?

这是程序集:

    .section    __TEXT,__text,regular,pure_instructions
    .globl  _main
    .align  4, 0x90
_main:                                  ## @main
    .cfi_startproc
## BB#0:
    pushq   %rbp
Ltmp2:
    .cfi_def_cfa_offset 16
Ltmp3:
    .cfi_offset %rbp, -16
    movq    %rsp, %rbp
Ltmp4:
    .cfi_def_cfa_register %rbp
    subq    $112, %rsp
    movq    ___stack_chk_guard@GOTPCREL(%rip), %rax
    movq    (%rax), %rax
    movq    %rax, -8(%rbp)
    leaq    -96(%rbp), %rax
    movl    $0, -12(%rbp)
    movl    %edi, -16(%rbp)
    movq    %rsi, -24(%rbp)
    movq    -24(%rbp), %rsi
    movq    8(%rsi), %rsi
    movq    %rax, %rdi
    callq   _strcpy
    leaq    L_.str(%rip), %rdi
    leaq    -96(%rbp), %rsi
    movq    %rax, -104(%rbp)        ## 8-byte Spill
    movb    $0, %al
    callq   _printf
    movq    ___stack_chk_guard@GOTPCREL(%rip), %rsi
    movq    (%rsi), %rsi
    movq    -8(%rbp), %rdi
    cmpq    %rdi, %rsi
    movl    %eax, -108(%rbp)        ## 4-byte Spill
    jne LBB0_2
## BB#1:                                ## %SP_return
    movl    $0, %eax
    addq    $112, %rsp
    popq    %rbp
    ret
LBB0_2:                                 ## %CallStackCheckFailBlk
    callq   ___stack_chk_fail
    .cfi_endproc

    .section    __TEXT,__cstring,cstring_literals
L_.str:                                 ## @.str
    .asciz   "buffer: %s\n"


.subsections_via_symbols

[更新]

实际上,似乎没有一个寄存器被覆盖,但看起来它们应该是:

启动程序:/Users/henrypitcairn/test2 python -c 'print "A"*128'

Breakpoint 1, 0x0000000100000ed4 in main ()
(gdb) c
Continuing.
buffer: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

Program received signal SIGABRT, Aborted.
0x00007fff8eef3866 in ?? ()
(gdb) info registers
rax            0x0  0
rbx            0x7fff77921310   140735199449872
rcx            0x7fff5fbff9f8   140734799804920
rdx            0x0  0
rsi            0x6  6
rdi            0xc07    3079
rbp            0x7fff5fbffa20   0x7fff5fbffa20
rsp            0x7fff5fbff9f8   0x7fff5fbff9f8
r8             0x0  0
r9             0x0  0
r10            0x8000000    134217728
r11            0x206    518
r12            0x0  0
r13            0x0  0
r14            0x6  6
r15            0x0  0
rip            0x7fff8eef3866   0x7fff8eef3866
eflags         0x206    [ PF IF ]
cs             0x7  7
ss             *value not available*
ds             *value not available*
es             *value not available*
fs             0x0  0
gs             0x30000  196608
(gdb) disas main
Dump of assembler code for function main:
   0x0000000100000ed0 <+0>: push   %rbp
   0x0000000100000ed1 <+1>: mov    %rsp,%rbp
   0x0000000100000ed4 <+4>: sub    $0x70,%rsp
   0x0000000100000ed8 <+8>: mov    0x131(%rip),%rax        # 0x100001010
   0x0000000100000edf <+15>:    mov    (%rax),%rax
   0x0000000100000ee2 <+18>:    mov    %rax,-0x8(%rbp)
   0x0000000100000ee6 <+22>:    lea    -0x60(%rbp),%rax
   0x0000000100000eea <+26>:    movl   $0x0,-0xc(%rbp)
   0x0000000100000ef1 <+33>:    mov    %edi,-0x10(%rbp)
   0x0000000100000ef4 <+36>:    mov    %rsi,-0x18(%rbp)
   0x0000000100000ef8 <+40>:    mov    -0x18(%rbp),%rsi
   0x0000000100000efc <+44>:    mov    0x8(%rsi),%rsi
   0x0000000100000f00 <+48>:    mov    %rax,%rdi
   0x0000000100000f03 <+51>:    callq  0x100000f54
   0x0000000100000f08 <+56>:    lea    0x7b(%rip),%rdi        # 0x100000f8a
   0x0000000100000f0f <+63>:    lea    -0x60(%rbp),%rsi
   0x0000000100000f13 <+67>:    mov    %rax,-0x68(%rbp)
   0x0000000100000f17 <+71>:    mov    $0x0,%al
   0x0000000100000f19 <+73>:    callq  0x100000f4e
   0x0000000100000f1e <+78>:    mov    0xeb(%rip),%rsi        # 0x100001010
   0x0000000100000f25 <+85>:    mov    (%rsi),%rsi
   0x0000000100000f28 <+88>:    mov    -0x8(%rbp),%rdi
   0x0000000100000f2c <+92>:    cmp    %rdi,%rsi
   0x0000000100000f2f <+95>:    mov    %eax,-0x6c(%rbp)
   0x0000000100000f32 <+98>:    jne    0x100000f43 <main+115>
   0x0000000100000f38 <+104>:   mov    $0x0,%eax
   0x0000000100000f3d <+109>:   add    $0x70,%rsp
   0x0000000100000f41 <+113>:   pop    %rbp
   0x0000000100000f42 <+114>:   retq   
   0x0000000100000f43 <+115>:   callq  0x100000f48
End of assembler dump.
(gdb) 

【问题讨论】:

  • 您始终可以在 ASM 级别逐步查看实际情况。
  • @OliCharlesworth 我只是仔细看了看。看起来确实应该覆盖某些内容,但我没有看到这种情况发生。
  • 我知道当你陷入未定义的行为时往往会发生有趣的事情,但这似乎很奇怪
  • 试图预测未定义的行为是一项棘手的工作,至少向我们展示您的 asm 代码,我们将能够看到编译器为缓冲区分配了多少空间。至于为什么,你为什么期望寄存器会溢出?
  • @Leeor 我将程序集添加到问题中。我希望某些寄存器会被覆盖,只是因为这是过去发生的事情,尽管我不知道为什么

标签: c macos buffer-overflow strcpy


【解决方案1】:

从您的汇编代码中,您可以看到编译器添加了一个 stack_chk_guard,为了获得更多“易于操作”的程序,请尝试使用 -fno-stack-protector 进行编译(假设它是 gcc)。更多选项(也在 llvm 中)是 here

它还表明编译器在堆栈上为局部变量保存了 112 个字节,其中包括 4 个字节和 8 个字节的寄存器溢出 - 所以其余 100 个字节可能是缓冲区 + 一些用于保护的填充,这可以解释为什么你看到它低于 100。

你实际上并没有在那里完成整个帧,你可能超出了溢出的寄存器,这解释了为什么它们在你以前的运行中似乎已经“改变”了,也可能为什么你得到一个 SIGABORT 而不是 seg错误以及为什么返回地址正常 - 你没有覆盖返回地址,你覆盖了一些寄存器(导致上帝知道什么)

【讨论】:

  • 我现在确实遇到了分段错误,但我仍然没有我期望的值。现在看来,rbp 已被覆盖。我认为 rbp 是当前堆栈帧的地址。为什么这是唯一有效的寄存器?
  • @735Tesla 一旦你的缓冲区溢出足够大,你可以覆盖一些寄存器(堆栈上的寄存器 - RIPRBP/RSP,以及任何函数参数通过这)。您似乎很惊讶它没有发生在 100 字节。
  • 这是我的问题的一部分。谢谢你的回答。你知道为什么我无法覆盖退货地址吗?
  • 好吧,我想通了,我只需要强制它返回给它的调用者。现在我也覆盖了 RIP,这正是我所希望的。 pastebin.com/CybHK4at
猜你喜欢
  • 2017-11-28
  • 1970-01-01
  • 2018-11-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-10-28
  • 2016-04-18
  • 2018-01-19
相关资源
最近更新 更多