修改存在缓冲区溢出漏洞的C代码以跳过代码。

4

我正在寻找一种方法来利用以下源代码中的缓冲区溢出漏洞,以便跳过printf("x is 1")这行:

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

void func(char *str) {
     char buffer[24];
     int *ret;
     strcpy(buffer,str);
}

int main(int argc, char **argv) {
    int x;
    x = 0;
    func(argv[1]);
    x = 1;
    printf("x is 1");
    printf("x is 0");
    getchar();
}

为了做到这一点,我想修改“func”函数。我知道我需要使用ret变量来修改返回地址,使其跳过我想要跳过的那一行,但我不确定如何实际操作。有人有建议吗?
编辑:
通过使用gdb,我在主函数中找到了以下调用:
Temporary breakpoint 1, 0x00000000004005ec in main ()
(gdb) x/20i $pc
=> 0x4005ec <main+4>:   sub    $0x20,%rsp
   0x4005f0 <main+8>:   mov    %edi,-0x14(%rbp)
   0x4005f3 <main+11>:  mov    %rsi,-0x20(%rbp)
   0x4005f7 <main+15>:  movl   $0x0,-0x4(%rbp)
   0x4005fe <main+22>:  mov    -0x20(%rbp),%rax
   0x400602 <main+26>:  add    $0x8,%rax
   0x400606 <main+30>:  mov    (%rax),%rax
   0x400609 <main+33>:  mov    %rax,%rdi
   0x40060c <main+36>:  callq  0x4005ac <func>
   0x400611 <main+41>:  movl   $0x1,-0x4(%rbp)
   0x400618 <main+48>:  mov    $0x4006ec,%edi
   0x40061d <main+53>:  mov    $0x0,%eax
   0x400622 <main+58>:  callq  0x400470 <printf@plt>
   0x400627 <main+63>:  mov    $0x4006f3,%edi
   0x40062c <main+68>:  mov    $0x0,%eax
   0x400631 <main+73>:  callq  0x400470 <printf@plt>
   0x400636 <main+78>:  callq  0x400490 <getchar@plt>
   0x40063b <main+83>:  leaveq
   0x40063c <main+84>:  retq
   0x40063d:    nop

尽管如此,我还是对下一步该怎么做感到困惑。我知道函数将返回到0x400611行,并且我需要使其跳转到0x400631,但我不确定应该跳多少位或者应该如何修改ret变量。


阅读此链接:http://insecure.org/stf/smashstack.html - nobody
@AndrewMedico,我看了你的例子,请看我的修改。我不确定如何确定将多少位添加到ret变量。 - user2276280
只需将代码 exec("/bin/sh", "sh", NULL); 写入缓冲区,并修改返回地址以跳转到缓冲区即可。 - Luis Colorado
1个回答

1
思路是找到函数返回地址在栈上的位置,然后将该地址加上你想获取的命令的偏移量。 具体步骤如下:
  1. 使用反汇编查找原始返回地址和新返回地址之间的差异:
  2. enter image description here

  3. 使用本地变量(例如函数参数)来查找堆栈上的函数帧地址:
  4. enter image description here

  5. 最终通过比较局部变量的地址来查找堆栈中返回地址的相对位置:
  6. enter image description here

使用以上方法,你的代码会像这样:
void func(char *str) {
    // 1. Get the address of an object on the stack
    long *ret = (long*)(&str);      

    // 2. Move ret to point to the location of the return address from this function. 
    //    Per the example above on my system (Windows 64bit + VS) it was just -1
    ret -= NUMBER_OF_ITEMS_IN_THE_STACK_BEFORE_RETURN_ADDR;

    // 3. Modify the return address by adding it the offset to command to go to (in my 
    //   (case 33).
    *ret = *ret + OFFSET_TO_COMMAND;

    // The rest of your code
    char buffer[24];
    strcpy(buffer, str);
}

如上所述,确切的数字取决于系统(即操作系统、编译器等)。然而,使用上述技术,您应该能够找到正确的数字进行设置。

最后需要注意的是,现代编译器(例如VS)可能具有保护堆栈损坏的安全机制。如果您的程序因此崩溃,请检查编译器选项以了解如何禁用此选项。


你能解释一下第一步后如何确定ret指向的地址吗?我试图将地址打印到命令行,但似乎与我的汇编转储中的任何内容都不匹配。当我将指针打印到命令行时,它看起来像“0xf201edd6”,但我的主函数转储都类似于“400600”。 - user2276280
我在我的答案中添加了一些图片,以使其更清晰。 - Amnon Shochot

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