无法在堆栈上写入(堆栈溢出)

7

我正在进行一些安全实验,特别是尝试理解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]

1
你是如何编译你的示例的?你使用的是哪个精确版本的GCC?它可能已经被编译器优化了..... - Basile Starynkevitch
我正在运行kali 32位和gcc(Debian 4.7.2-5)4.7.2。我是这样编译的:“gcc -ggdb -z execstack -fno-stack-protector -o prog exploit.c”(ASLR已关闭)。刚刚5分钟前,我尝试添加“-mpreferred-stack-boundary = 2”,并将缓冲区大小调整为20个字符。令我惊讶的是,在gdb中,我可以使用200个字符返回到主函数并执行其余部分...? - user3102158
由于这不应该发生。我建议您还要添加如何填充缓冲区的方法。您可能使用一些Perl习语,但是您的程序确切执行方式是什么?(如果您不打算提供实际地址,则对齐堆栈显然无关紧要;如果您不尝试插入/执行shell代码,则nx位设置与否无关紧要)。 - gnometorule
在第一次编辑中,我不明白为什么在执行ret之前甚至只有1个字符溢出时就会出现段错误?第二次编辑更令人困扰,使用200个输入字符时我没有遇到任何段错误...? - user3102158
1
请在实际开始将输入复制到缓冲区之前和之后打印堆栈。 - Gari BN
显示剩余2条评论
1个回答

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

我的电池快没电了,我不能严格完成这个问题。但是,考虑这些方面。

编辑/交叉电池手指:还有,第66次写操作可能已经拉入0,因为两边都受到i污染的影响(在相对于&buffer存储它并相对于argv[1][0]读取它的位置上)。


1
听起来非常合理!我也认为这是原因。可以通过在运行期间检查i的值轻松验证。 - Gari BN
@gnometorule 看看 i 的值。它是 0x00410043,所以我不明白为什么你对自己的答案不确定。它要么在尝试读取 argv[0] + 0x00410043 时得到 PF,要么就是存储在那里的 0 - qwm
看起来你是对的,我已经打印了 i,当我输入 65 个字符时,i 从 64 开始,直接跳到了 66,而 66 是 argv1 的结尾。对于第二段代码,变量 i 也受到影响。只有 20 个保留字符的溢出,i 就从 0 到 20,从 66 到 67。我猜它可能实际上写入了环境变量。我尝试更改变量的顺序,但它并没有影响问题。在这里,我认为变量被按照代码中的相同顺序推送,有没有办法这样做? - user3102158
就像gnometorule所说的那样,i存储在比堆栈中的buff[]更低的地址中,并且正在被污染。那么我该如何反转顺序,使得buff[]直接放置在$ebp之后,以便i不再受溢出的影响?(我已经尝试更改C代码中变量的顺序,但没有任何变化) - user3102158
例如:@user3102158 struct { int i; char buf[0x40]; } s; - qwm
显示剩余5条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接