【问题标题】:impossible to write on stack (stack overflow)无法写入堆栈(堆栈溢出)
【发布时间】:2014-02-01 21:51:23
【问题描述】:

我正在试验一些安全性的东西,尤其是试图理解一个 ret2ret 漏洞。 我正在试验的代码:

void foo(char * val){
        char buffer[64];
        int i;
        for (i=0; val[i]!=0; i++) buffer[i]=val[i];
        return;
}

int main(int argc, char ** argv) {
        foo(argv[1]);
        return 0;
} 

ASLR、N^X 和堆栈金丝雀在我的测试期间关闭。我用 gcc 将它编译成 32 位。 我不知道为什么,但我无法得到通常的“0x41414141 in ?? ()”,说我已经覆盖了 $eip。所以我决定用 gdb 调试并在函数 "cop" 的 ret 上放一个断点,奇怪的是,即使在写了 300 多个 "A" 之后堆栈是这样的:

 0xbffff46c:    0xb7ee2290  0xbffff496  0xb7e8f5f5  0x41414141
 0xbffff47c:    0x41414141  0x41414141  0x41414141  0x41414141
 0xbffff48c:    0x41414141  0x41414141  0x41414141  0x41414141
 0xbffff49c:    0x41414141  0x41414141  0x41414141  0x41414141
 0xbffff4ac:    0x41414141  0x41414141  0x41414141  0x00410043

缓冲区对应的64个字符都在这里,但其余的没有写入..我不知道为什么?是因为某种更新吗?

编辑:buff[64] 的 GDB 日志

Dump of assembler code for function main:
   0x08048415 <+0>: push   %ebp
   0x08048416 <+1>: mov    %esp,%ebp
   0x08048418 <+3>: sub    $0x4,%esp
   0x0804841b <+6>: mov    0xc(%ebp),%eax
   0x0804841e <+9>: add    $0x4,%eax
   0x08048421 <+12>:    mov    (%eax),%eax
   0x08048423 <+14>:    mov    %eax,(%esp)
   0x08048426 <+17>:    call   0x80483dc <foo>
   0x0804842b <+22>:    mov    $0x0,%eax
   0x08048430 <+27>:    leave  
   0x08048431 <+28>:    ret 

Dump of assembler code for function foo:
   0x080483dc <+0>: push   %ebp
   0x080483dd <+1>: mov    %esp,%ebp
   0x080483df <+3>: sub    $0x44,%esp
   0x080483e2 <+6>: movl   $0x0,-0x4(%ebp)
   0x080483e9 <+13>:    jmp    0x8048404 <foo+40>
   0x080483eb <+15>:    mov    -0x4(%ebp),%edx
   0x080483ee <+18>:    mov    0x8(%ebp),%eax
   0x080483f1 <+21>:    add    %edx,%eax
   0x080483f3 <+23>:    movzbl (%eax),%eax
   0x080483f6 <+26>:    lea    -0x44(%ebp),%ecx
   0x080483f9 <+29>:    mov    -0x4(%ebp),%edx
   0x080483fc <+32>:    add    %ecx,%edx
   0x080483fe <+34>:    mov    %al,(%edx)
   0x08048400 <+36>:    addl   $0x1,-0x4(%ebp)
   0x08048404 <+40>:    mov    -0x4(%ebp),%edx
   0x08048407 <+43>:    mov    0x8(%ebp),%eax
   0x0804840a <+46>:    add    %edx,%eax
   0x0804840c <+48>:    movzbl (%eax),%eax
   0x0804840f <+51>:    test   %al,%al
   0x08048411 <+53>:    jne    0x80483eb <foo+15>
   0x08048413 <+55>:    leave  
   0x08048414 <+56>:    ret   

(gdb) b *foo+56
 Breakpoint 1 at 0x8048414: file exploit.c, line 9.

(gdb) r `python -c 'print "A"*64'`
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /root/prog `python -c 'print "A"*64'`

Breakpoint 1, 0x08048414 in foo (arg=0xbffff6da 'A' <repeats 64 times>) at exploit.c:9
9   }
(gdb) r `python -c 'print "A"*65'`
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /root/prog `python -c 'print "A"*65'`

Program received signal SIGSEGV, Segmentation fault.
0x0804840c in foo (arg=0xbffff6d9 'A' <repeats 65 times>) at exploit.c:6
6       for(i = 0; arg[i] != 0; i++) buff[i] = arg[i];

编辑 2:buff[20] 的 GDB 日志

(gdb) disas foo
Dump of assembler code for function foo:
   0x080483dc <+0>: push   %ebp
   0x080483dd <+1>: mov    %esp,%ebp
   0x080483df <+3>: sub    $0x18,%esp
   0x080483e2 <+6>: movl   $0x0,-0x4(%ebp)
   0x080483e9 <+13>:    jmp    0x8048404 <foo+40>
   0x080483eb <+15>:    mov    -0x4(%ebp),%edx
   0x080483ee <+18>:    mov    0x8(%ebp),%eax
   0x080483f1 <+21>:    add    %edx,%eax
   0x080483f3 <+23>:    movzbl (%eax),%eax
   0x080483f6 <+26>:    lea    -0x18(%ebp),%ecx
   0x080483f9 <+29>:    mov    -0x4(%ebp),%edx
   0x080483fc <+32>:    add    %ecx,%edx
   0x080483fe <+34>:    mov    %al,(%edx)
   0x08048400 <+36>:    addl   $0x1,-0x4(%ebp)
   0x08048404 <+40>:    mov    -0x4(%ebp),%edx
   0x08048407 <+43>:    mov    0x8(%ebp),%eax
   0x0804840a <+46>:    add    %edx,%eax
   0x0804840c <+48>:    movzbl (%eax),%eax
   0x0804840f <+51>:    test   %al,%al
   0x08048411 <+53>:    jne    0x80483eb <foo+15>
   0x08048413 <+55>:    leave  
   0x08048414 <+56>:    ret    
End of assembler dump.
(gdb) b *foo+56
Breakpoint 1 at 0x8048414: file exploit.c, line 9.
(gdb) r `python -c 'print "A"*200'`
Starting program: /root/prog `python -c 'print "A"*200'`

Breakpoint 1, 0x08048414 in foo (arg=0xbffff652 'A' <repeats 200 times>) at exploit.c:9
9   }
(gdb) c
Continuing.
[Inferior 1 (process 3474) exited normally]

【问题讨论】:

  • 你是如何编译你的例子的?您使用的是哪个精确版本的 GCC?它可能已经被编译器优化了.....
  • 我在 kali 32 位和 gcc (Debian 4.7.2-5) 4.7.2 下运行。我这样编译它:“gcc -ggdb -z execstack -fno-stack-protector -o prog exploit.c”(ASLR 关闭)。我刚刚尝试了 500 万之前添加“-mpreferred-stack-boundary=2”并将缓冲区大小调整为 20 个字符。我很惊讶在 gdb 中看到 200 个字符我仍然可以返回到 main 并执行其余的......?
  • 可以提供gdb日志吗?当然 - 取消功能。
  • 因为这不应该发生。我建议你也添加你如何喂你的缓冲区。您可能使用了一些 perl 习惯用法,但是您究竟是如何执行您的程序的呢? (如果您不打算提供实际地址,则对齐堆栈显然无关紧要;如果您不尝试插入/执行 shell 代码,则设置或不设置 nx 位无关紧要)。
  • 请在实际开始将输入复制到缓冲区之前和之后打印堆栈。

标签: c security assembly buffer-overflow fortify-source


【解决方案1】:

我想我明白了,至少对于 64 位缓冲区。您的计数变量 i 在堆栈上的位置高于您的缓冲区(根据您的反汇编)。这意味着,您的第 65 个存储更改了 i 的值。请注意,它不会是 i 的整个值,因为它可能是一个 4 字节整数;所以只有低字节(小端)。无论如何,之后,就好像您将 i 计数得足够多,以至于下一次写入 (66) 应该指向由环境变量填充的区域(过去 ret),这是无害的并且不会污染 eip。

我的batts快完成了,我不能严格完成这个。但请按照这些思路思考。

编辑/交叉手指:此外,第 66 次写入可能已经拉入 0,因为双方都受到 i 污染的影响(您将其存储在相对于 &buffer; 您从相对于 argv[1 ][0]。

【讨论】:

  • 听起来很合理!我也觉得是这个原因。可以通过在运行期间检查 i 的值来轻松验证。
  • @gnometorule 查看i 的值。它是0x00410043,所以我不明白你为什么不确定自己的答案。它要么在尝试读取 argv[0] + 0x00410043 时得到 PF,要么是存储在那里的 0
  • 是的,看起来你是对的,我已经打印了 i 并且当我输入 65 个字符时,我从 64 跳到 66 而没有经过 65,而 66 是 argv[1] 的结尾('\0')。对于第二个代码,变量 i 也受到影响。仅 20 个保留字符溢出 70 个字符,i 从 0 到 20,从 66 到 67。我猜它可能实际上是在环境变量中写入。我试图改变变量的顺序,但它不会影响问题。在这里,我认为变量的推送顺序与代码中的顺序相同,有没有办法做到这一点?
  • @user3102158 你是什么意思?它只是编译您编写的代码。如果您包含任何性能优化标志i 肯定会包含在寄存器中,因此您的缓冲区溢出实际上会如您预期的那样发生
  • 就像 gnometorule 说的 i 存储在堆栈中比 buff[] 更低的地址并且正在被污染。那么我怎样才能颠倒顺序,使 buff[] 直接放在 $ebp 之后,这样我就不再受到溢出的影响了? (我试图改变我的 C 代码中变量的顺序,但没有改变)
猜你喜欢
  • 2019-05-18
  • 2011-11-20
  • 2012-04-15
  • 2012-12-20
  • 2011-03-02
  • 2011-08-22
  • 2023-03-04
  • 2014-01-17
相关资源
最近更新 更多