修改具有缓冲区溢出漏洞的 C 代码以跳过代码

信息安全 开发 缓冲区溢出 C
2021-09-03 13:33:10

我试图找到一种方法来利用以下源代码中的缓冲区溢出漏洞,因此将跳过 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,我能够在 main 函数中找到以下调用:

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,但我不确定如何确定要跳转多少位。我一直在尝试遵循 insecure.org/stf/smashstack.html 中的示例 3,但是在弄清楚要调整变量的位数时我迷失了方向。有什么帮助吗?

2个回答

正如您已经确定的那样,您需要返回地址 0x400631。当你用你的论点粉碎堆栈时,你应该控制 EIP。IE:

./a.out AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

您应该会看到 0x41414141 的 EIP。现在您需要确定成为 EIP 的 4 个字节在 A 的堆栈中的位置。您可以使用 metasploit 的 pattern_create.rb/pattern_offset.rb 之类的工具,或者因为它是一个很小的缓冲区,您可以手动完成:

./a.out AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPPQQQQRRRRSSSSTTTT

根据 EIP 的地址,您现在可以确定它是 A (x41)、B (x42)、C (x43) 等。这将告诉您需要在 \x31\x06\x40\x00 之前放置多少字节. 这当然不是一个可靠的利用,因为返回地址是硬编码的,但它足以满足您的学习示例。

虽然我不同意你的计划,

跳过一条指令的方法是修改调用者的 pc 寄存器值,该值保存在被调用函数的堆栈中

所以跳过一条指令相当简单,因为指令大小(在现代 CPU 中)是相对一致的。

然而,跳过一行 C 代码是一个完全不同的问题,因为该行可以包含任意数量的指令。

许多指令可以根据编译器参数(例如优化级别)而改变。