简体   繁体   English

使用fgets导致缓冲区溢出

[英]Causing a buffer Overflow with fgets

I'm experimenting with buffer overflows and try to overwrite the return address of the stack with a certain input of fgets 我正在尝试缓冲区溢出,并尝试使用一定的fgets输入覆盖堆栈的返回地址

This is the code: 这是代码:

void foo()
{
    fprintf(stderr, "You did it.\n");
}

void bar()
{
    char buf[20];
    puts("Input:");
    fgets(buf, 24, stdin);
    printf("Your input:.\n", strlen(buf));
}


int main(int argc, char **argv)
{
    bar();
    return 0;
}

On a normal execution the program just returns your input. 在正常执行时,程序只返回您的输入。 I want it to output foo() without modifying the code. 我希望它输出foo()而不修改代码。

My idea was to overflow the buffer of buf by entering 20 'A' s. 我的想法是溢出的缓冲区buf通过输入20 'A'秒。 This works and causes a segmentation fault. 这会起作用并导致分段错误。 My next idea was to find out the address of foo() which is \\x4006cd and append this to the 20 'A' s. 我的下一个想法是找出foo()的地址是\\x4006cd并将其附加到\\x4006cd 'A'

From my understanding this should overwrite the return address of the stack and make it jump to foo . 根据我的理解,这应该覆盖堆栈的返回地址并使其跳转到foo But it only causes a segfault. 但它只会导致段错误。

What am I doing wrong? 我究竟做错了什么?

Update: Assembler dumps main 更新:汇编程序转储主要

    Dump of assembler code for function main:
   0x000000000040073b <+0>: push   %rbp
   0x000000000040073c <+1>: mov    %rsp,%rbp
   0x000000000040073f <+4>: sub    $0x10,%rsp
   0x0000000000400743 <+8>: mov    %edi,-0x4(%rbp)
   0x0000000000400746 <+11>:    mov    %rsi,-0x10(%rbp)
   0x000000000040074a <+15>:    mov    $0x0,%eax
   0x000000000040074f <+20>:    callq  0x4006f1 <bar>
   0x0000000000400754 <+25>:    mov    $0x0,%eax
   0x0000000000400759 <+30>:    leaveq 
   0x000000000040075a <+31>:    retq   
   End of assembler dump.

foo FOO

Dump of assembler code for function foo:
   0x00000000004006cd <+0>: push   %rbp
   0x00000000004006ce <+1>: mov    %rsp,%rbp
   0x00000000004006d1 <+4>: mov    0x200990(%rip),%rax        # 0x601068 <stderr@@GLIBC_2.2.5>
   0x00000000004006d8 <+11>:    mov    %rax,%rcx
   0x00000000004006db <+14>:    mov    $0x15,%edx
   0x00000000004006e0 <+19>:    mov    $0x1,%esi
   0x00000000004006e5 <+24>:    mov    $0x400804,%edi
   0x00000000004006ea <+29>:    callq  0x4005d0 <fwrite@plt>
   0x00000000004006ef <+34>:    pop    %rbp
   0x00000000004006f0 <+35>:    retq   
End of assembler dump.

bar: 酒吧:

Dump of assembler code for function bar:
   0x00000000004006f1 <+0>: push   %rbp
   0x00000000004006f2 <+1>: mov    %rsp,%rbp
   0x00000000004006f5 <+4>: sub    $0x20,%rsp
   0x00000000004006f9 <+8>: mov    $0x40081a,%edi
   0x00000000004006fe <+13>:    callq  0x400570 <puts@plt>
   0x0000000000400703 <+18>:    mov    0x200956(%rip),%rdx        # 0x601060 <stdin@@GLIBC_2.2.5>
   0x000000000040070a <+25>:    lea    -0x20(%rbp),%rax
   0x000000000040070e <+29>:    mov    $0x18,%esi
   0x0000000000400713 <+34>:    mov    %rax,%rdi
   0x0000000000400716 <+37>:    callq  0x4005b0 <fgets@plt>
   0x000000000040071b <+42>:    lea    -0x20(%rbp),%rax
   0x000000000040071f <+46>:    mov    %rax,%rdi
   0x0000000000400722 <+49>:    callq  0x400580 <strlen@plt>
   0x0000000000400727 <+54>:    mov    %rax,%rsi
   0x000000000040072a <+57>:    mov    $0x400821,%edi
   0x000000000040072f <+62>:    mov    $0x0,%eax
   0x0000000000400734 <+67>:    callq  0x400590 <printf@plt>
   0x0000000000400739 <+72>:    leaveq 
   0x000000000040073a <+73>:    retq   
End of assembler dump.

You did not count with memory aligment. 你不记得内存对齐。 I changed the code a litte bit to make it easier to find the right spot. 我将代码更改了一点,以便更容易找到正确的位置。

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

int **x;
int z;

void foo()
{
    fprintf(stderr, "You did it.\n");
}

void bar()
{
    char buf[2];
    //puts("Input:");
    //fgets(buf, 70, stdin);
    x = (int**) buf;
    for(z=0;z<8;z++)
            printf("%d X=%x\n", z, *(x+z));
    *(x+3) = foo;
    printf("Your input: %d %s\n", strlen(buf), buf);
}


int main(int argc, char **argv)
{
        printf("Foo: %x\n", foo);
        printf("Main: %x\n", main);
        bar();
        return 0;
}

With a smaller buffer, 2 in my example, I found the return address 24 bytes away (x+3, for 8 byte pointers; 64 bits, no debug, no optimization...) from the beginning of the buffer. 使用较小的缓冲区,在我的示例中为2,我从缓冲区的开头找到了24字节的返回地址(x + 3,对于8字节指针; 64位,无调试,没有优化...)。 This position can change depending on the buffer size, architecture, etc. In this example, I manage to change the return address of bar to foo. 这个位置可以根据缓冲区大小,体系结构等而改变。在这个例子中,我设法将bar的返回地址更改为foo。 Anyway you will get a segmentation fault at foo return, as it was not properly set to return to main. 无论如何,你会在foo返回时得到一个分段错误,因为它没有正确设置为返回main。

I added x and z as global vars to not change the stack size of bar. 我添加了x和z作为全局变量,不改变bar的堆栈大小。 The code will display an array of pointer like values, starting at buf[0]. 代码将显示一个类似指针的数组,从buf [0]开始。 In my case, I found the address in main in the position 3. That's why the final code has *(x+3) = foo. 在我的情况下,我在主要位置3找到了地址。这就是为什么最终代码有*(x + 3)= foo。 As I said, this position can change depending on compilation options, machine etc. To find the correct position, find the address of main (printed before calling bar) on the address list. 正如我所说,这个位置可能会根据编译选项,机器等而改变。要找到正确的位置,请在地址列表中找到main的地址(在调用条之前打印)。

It is important to note that I said address in main, not the address of main, bacause the return address was set to the line after the call to bar and not to the beginning of main. 重要的是要注意我在main中的地址,而不是main的地址,因为返回地址设置为调用bar之后的行而不是main的开头。 So, in my case, it was 0x4006af instead of 0x400668. 所以,在我的情况下,它是0x4006af而不是0x400668。

In your example, with 20 bytes buffer, as far as I know, it was aligned to 32 bytes (0x20). 在你的例子中,有20个字节的缓冲区,据我所知,它被对齐到32个字节(0x20)。

If you want to do the same with fgets, you have to figure out how to type the address of foo, but if you are running a x86/x64 machine, remember to add it in little enddian. 如果你想对fgets做同样的事情,你必须弄清楚如何输入foo的地址,但是如果你正在运行x86 / x64机器,记得把它添加到little enddian中。 You can change the code to display the values byte per byte, so you can get them in the right order and type them using ALT+number. 您可以更改代码以显示每个字节的值字节,因此您可以按正确的顺序获取它们并使用ALT +编号键入它们。 Remember that the numbers you type while holding ALT are decimal numbers. 请记住,您在按住ALT时键入的数字是十进制数字。 Some terminals won't be friendly handling 0x00. 有些终端不会友好处理0x00。

My output looks like: 我的输出看起来像:

$ gcc test.c -o test
test.c: In function ‘bar’:
test.c:21: warning: assignment from incompatible pointer type
$ ./test
Foo: 400594
Main: 400668
0 X=9560e9f0
1 X=95821188
2 X=889350f0
3 X=4006af
4 X=889351d8
5 X=0
6 X=0
7 X=95a1ed1d
Your input: 5 ▒▒`▒9
You did it.
Segmentation fault
 void bar() { char buf[20]; puts("Input:"); fgets(buf, 24, stdin); printf("Your input:.\\n", strlen(buf)); } 

... This works and causes a segmentation fault... ...这有效并导致分段错误......

The compiler is probably replacing fgets with a safer variant that includes a check on the destination buffer size. 编译器可能用更安全的变体替换fgets ,其中包括检查目标缓冲区大小。 If the check fails, the the prgram unconditionally calls abort() . 如果检查失败,则prgram无条件地调用abort()

In this particular case, you should compile the program with -U_FORTIFY_SOURCE or -D_FORTIFY_SOURCE=0 . 在这种特殊情况下,您应该使用-U_FORTIFY_SOURCE-D_FORTIFY_SOURCE=0编译程序。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM