堆栈居民缓冲区溢出64位? [英] Stack resident buffer overflow on 64-bit?

查看:155
本文介绍了堆栈居民缓冲区溢出64位?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在学习一些安全相关的事情,现在我摆弄我自己的堆栈。我在做什么应该是很琐碎,我还没有尝试执行的栈,只是为了证明我可以控制我的64位系统的指令指针。我已经关闭了我所知道的一切保护机制只是为了能够与它(NX位,ASLR,还与-fno-堆栈保护-z execstack编译)来播放。 我没有与64位汇编和花一些时间搜索和尝试自己,我想知道如果任何人都可以阐明一个问题,我遇到了一些光在那之后很多经验。

I'm studying some security related things and right now I'm playing around with my own stack. What I'm doing should be very trivial, I'm not even trying to execute the stack, simply to show that I can get control over the instruction pointer on my 64-bit system. I have turned off all protection mechanisms I'm aware of just to be able to play with it (NX-bit, ASLR, also compiling with -fno-stack-protector -z execstack). I don't have that much experience with 64-bit assembly and after spending some time searching and experimenting myself I'm wondering if anyone could shed some light on an issue I'm experiencing.

我有一个程序(来源$ C ​​$ C以下),它简单地复制一个字符串与无边界检查堆栈居民缓冲区。然而,当我重写了一系列的0x41我期待看到的RIP设置为0x4141414141414141,而是我发现我的RBP被设置为该值。我得到一个分段错误,但RIP没有更新这个(非法的)值在RET指令的执行,即使RSP设置为一个法律价值。我甚至在验证GDB有含紧接RET指令了一系列的0x41的在RSP readlable内存。

I have a program (source code below) which simply copies a string into a stack resident buffer with no bounds checking. However when I overwrite with a series of 0x41 I'm expecting to see the RIP be set to 0x4141414141414141, instead I'm finding that my RBP gets set to this value. I do get a segmentation fault, but RIP does not get updated to this (illegal) value at the execution of the RET instruction, even if RSP is set to a legal value. I have even verified in GDB that there is readlable memory containing a series of 0x41's at RSP immediately prior to the RET instruction.

我下的是IM pression的LEAVE指令所做的:

I was under the impression that the LEAVE instruction did:

MOV(E)SP,(E)BP

MOV (E)SP, (E)BP

POP(E)BP

然而在64位,将LEAVEQ指令似乎做(类似):

However on 64-bit, the "LEAVEQ" instruction seems to do (similar to):

MOV RBP,QWORD PTR [RSP]

MOV RBP, QWORD PTR [RSP]

我想它这样做只是从之前和执行该指令后,观察所有寄存器的内容。 LEAVEQ似乎是RET指令只是一个上下文相关的名字,但(这GDB的反汇编给它),因为它仍然只是一个0xC9。

I'm thinking it does this simply from observing the contents of all registers before and after execution of this instruction. LEAVEQ seems to be just a context dependent name of the RET instruction though (which GDB's disassembler gives it), as it is still just a 0xC9.

和RET指令似乎做一些与RBP寄存器,也许提领吗?我下的是IM pression的RET做(类似):

And the RET instruction seems to do something with the RBP register, perhaps dereferencing it? I was under the impression that RET did (similar to):

MOV RIP,QWORD PTR [RSP]

MOV RIP, QWORD PTR [RSP]

不过就像我提到的,似乎取消引用RBP,我想它这样做,因为我得到当没有其它寄存器似乎包含非法值分割故障。

However like I mentioned, it seems to dereference RBP, I'm thinking it does this because I get a segmentation fault when no other register seems to contain an illegal value.

来源$ C ​​$ C的程序:

Source code for the program:

#include <stdio.h>
#include <string.h>

int vuln_function(int argc,char *argv[])
{
    char buffer[512];

    for(int i = 0; i < 512; i++) {
        buffer[i] = 0x42;
    }

    printf("The buffer is at %p\n",buffer);

    if(argc > 1) {
        strcpy(buffer,argv[1]);
    }

    return 0;
}    

int main(int argc,char *argv[])
{
    vuln_function(argc,argv);

    return 0;
}

for循环是只是为了填满缓冲区的0x42,它可以很容易地看到在调试器中它是法律的一部分,溢出了。

The for loop is just there to fill the legal part of the buffer with 0x42, which makes it easy to see in the debugger where it is, before the overflow.

调试会话的摘译如下:

(gdb) disas vulnerable
Dump of assembler code for function vulnerable:
   0x000000000040056c <+0>:     push   rbp
   0x000000000040056d <+1>:     mov    rbp,rsp
   0x0000000000400570 <+4>:     sub    rsp,0x220
   0x0000000000400577 <+11>:    mov    DWORD PTR [rbp-0x214],edi
   0x000000000040057d <+17>:    mov    QWORD PTR [rbp-0x220],rsi
   0x0000000000400584 <+24>:    mov    DWORD PTR [rbp-0x4],0x0
   0x000000000040058b <+31>:    jmp    0x40059e <vulnerable+50>
   0x000000000040058d <+33>:    mov    eax,DWORD PTR [rbp-0x4]
   0x0000000000400590 <+36>:    cdqe   
   0x0000000000400592 <+38>:    mov    BYTE PTR [rbp+rax*1-0x210],0x42
   0x000000000040059a <+46>:    add    DWORD PTR [rbp-0x4],0x1
   0x000000000040059e <+50>:    cmp    DWORD PTR [rbp-0x4],0x1ff
   0x00000000004005a5 <+57>:    jle    0x40058d <vulnerable+33>
   0x00000000004005a7 <+59>:    lea    rax,[rbp-0x210]
   0x00000000004005ae <+66>:    mov    rsi,rax
   0x00000000004005b1 <+69>:    mov    edi,0x40070c
   0x00000000004005b6 <+74>:    mov    eax,0x0
   0x00000000004005bb <+79>:    call   0x4003d8 <printf@plt>
   0x00000000004005c0 <+84>:    cmp    DWORD PTR [rbp-0x214],0x1
   0x00000000004005c7 <+91>:    jle    0x4005e9 <vulnerable+125>
   0x00000000004005c9 <+93>:    mov    rax,QWORD PTR [rbp-0x220]
   0x00000000004005d0 <+100>:   add    rax,0x8
   0x00000000004005d4 <+104>:   mov    rdx,QWORD PTR [rax]
   0x00000000004005d7 <+107>:   lea    rax,[rbp-0x210]
   0x00000000004005de <+114>:   mov    rsi,rdx
   0x00000000004005e1 <+117>:   mov    rdi,rax
   0x00000000004005e4 <+120>:   call   0x4003f8 <strcpy@plt>
   0x00000000004005e9 <+125>:   mov    eax,0x0
   0x00000000004005ee <+130>:   leave  
   0x00000000004005ef <+131>:   ret    

我打破调用的strcpy()前右,但经过缓冲器已经充满的0x42的。

I break right before the call to strcpy(), but after the buffer has been filled with 0x42's.

(gdb) break *0x00000000004005e1

该方案是与650×41的作为参数执行,这应该是足够覆盖堆栈上的返回地址。

The program is executed with 650 0x41's as argument, this should be plenty to overwrite the return address on the stack.

(gdb) run `perl -e 'print "A"x650'`

我搜索的返回地址0x00400610内存(这是我发现的望着主的拆卸)。

I search the memory for the return address 0x00400610 (which I found from looking at the disassembly of main).

(gdb) find $rsp, +1024, 0x00400610
0x7fffffffda98
1 pattern found.

我检查与X / 200X内存,并得到一个很好的概述,我已经在这里省略,因为它的大小,但我可以清楚地看到表示缓冲区的合法大小,返回地址的0x42。

I examine the memory with x/200x and get a nice overview which I have omitted here because of its size, but I can clearly see the 0x42 that denote the legal size of the buffer, and the return address.

0x7fffffffda90: 0xffffdab0      0x00007fff      0x00400610      0x00000000

新的断点刚过的strcpy():

New breakpoint just after strcpy():

(gdb) break *0x00000000004005e9
(gdb) set disassemble-next-line on
(gdb) si
19 }
=> 0x00000000004005ee <vulnerable+130>:  c9     leave  
   0x00000000004005ef <vulnerable+131>:  c3     ret    
(gdb) i r
rax            0x0      0
rbx            0x0      0
rcx            0x4141414141414141       4702111234474983745
rdx            0x414141 4276545
rsi            0x7fffffffe17a   140737488347514
rdi            0x7fffffffdb00   140737488345856
rbp            0x7fffffffda90   0x7fffffffda90
rsp            0x7fffffffd870   0x7fffffffd870
r8             0x1      1
r9             0x270    624
r10            0x6      6
r11            0x7ffff7b9fff0   140737349550064
r12            0x400410 4195344
r13            0x7fffffffdb90   140737488346000
r14            0x0      0
r15            0x0      0
rip            0x4005ee 0x4005ee <vulnerable+130>

   0x00000000004005ee <vulnerable+130>:  c9     leave  
=> 0x00000000004005ef <vulnerable+131>:  c3     ret    
(gdb) i r
rax            0x0      0
rbx            0x0      0
rcx            0x4141414141414141       4702111234474983745
rdx            0x414141 4276545
rsi            0x7fffffffe17a   140737488347514
rdi            0x7fffffffdb00   140737488345856
rbp            0x4141414141414141       0x4141414141414141
rsp            0x7fffffffda98   0x7fffffffda98
r8             0x1      1
r9             0x270    624
r10            0x6      6
r11            0x7ffff7b9fff0   140737349550064
r12            0x400410 4195344
r13            0x7fffffffdb90   140737488346000
r14            0x0      0
r15            0x0      0
rip            0x4005ef 0x4005ef <vulnerable+131>
(gdb) si

Program received signal SIGSEGV, Segmentation fault.
   0x00000000004005ee <vulnerable+130>:  c9     leave  
=> 0x00000000004005ef <vulnerable+131>:  c3     ret    
(gdb) i r
rax            0x0      0
rbx            0x0      0
rcx            0x4141414141414141       4702111234474983745
rdx            0x414141 4276545
rsi            0x7fffffffe17a   140737488347514
rdi            0x7fffffffdb00   140737488345856
rbp            0x4141414141414141       0x4141414141414141
rsp            0x7fffffffda98   0x7fffffffda98
r8             0x1      1
r9             0x270    624
r10            0x6      6
r11            0x7ffff7b9fff0   140737349550064
r12            0x400410 4195344
r13            0x7fffffffdb90   140737488346000
r14            0x0      0
r15            0x0      0
rip            0x4005ef 0x4005ef <vulnerable+131>

我验证返回地址已被覆盖,我应该期望看到RIP被置到这个地址:

I verify that the return address has been overwritten and I should have expected to see RIP get set to this address:

(gdb) x/4x 0x7fffffffda90
0x7fffffffda90: 0x41414141      0x41414141      0x41414141      0x41414141
(gdb) x/4x $rsp          
0x7fffffffda98: 0x41414141      0x41414141      0x41414141      0x41414141

然而,RIP显然是:

Yet RIP is clearly:

rip            0x4005ef 0x4005ef <vulnerable+131>

为什么没有RIP得到更新,我期待?什么是LEAVEQ和RETQ真正做到在64位?简而言之,我在这里丢失?我曾尝试编译就当看它是否有什么差别省略编译参数,它似乎没有任何区别。

Why has not RIP gotten updated as I'm expecting? What does LEAVEQ and RETQ really do on 64-bit? In short, what am I missing here? I have tried to omit the compiler arguments when compiling just to see if it makes any difference, it doesn't seem to make any difference.

推荐答案

这两个指令都做的正是你希望他们做的事。你已经覆盖了previous堆栈帧 0×41 的,所以当你点击 leaveq ,你在做什么这样的:

Those two instructions are doing exactly what you expect them to do. You have overwritten the previous stack frame with 0x41's so when you hit the leaveq, you are doing this:

mov rsp, rbp
pop rpb

现在 RSP 点,其中, RBP 以前那样。但是,你已经覆盖的区域内存的,所以当你做了弹出RBP ,硬件基本上是这样

Now rsp points to where rbp did before. However, you have overwritten that region of memory, so when you do the pop rbp, the hardware is essentially doing this

mov rbp, [rsp]
add rsp,1

[RSP] 现在有 0×41 的。所以这就是为什么你会看到 RBP 得到填补与价值。

But [rsp] now has 0x41's. So this is why you're seeing rbp get filled with that value.

至于为什么 RIP 是没有得到设置像你期望的那样,这是因为 RET 是设置裂口 0×41 ,然后产生一个异常(缺页)上取指令。我不会依赖于GDB显示正确的事,在这种情况下。你应该尝试与程序的文本段内的有效地址改写返回值,你可能不会看到这种怪异的行为。

As for why rip isn't getting set like you expect, it's because ret is setting the rip to 0x41 and then generating an exception (page fault) on the instruction fetch. I wouldn't rely on GDB to show the right thing in this case. You should try overwriting the return value with a valid address within the program's text segment and you likely won't see this weird behavior.

这篇关于堆栈居民缓冲区溢出64位?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆