覆盖堆栈上存储的 return 地址不起作用

Overwriting the stored return address on the stack doesn't work

我试图通过覆盖堆栈上存储的 return 地址 (saved eip) 来控制 C 程序的执行:

(gdb) info frame
Stack level 0, frame at 0xbffff550:
 eip = 0x8048831 in main (6.c:52); saved eip = 0xbffffdef
 source language c.
 Arglist at 0xbffff538, args: argc=6, argv=0xbffff5e4
 Locals at 0xbffff538, Previous frame's sp is 0xbffff550
 Saved registers:
  ebx at 0xbffff534, ebp at 0xbffff538, eip at 0xbffff54c

0xbffffdef 是一个 "Hello, world!" shellcode 的地址,它已被组装,检查任何空字节并放入环境变量 SHELLCODE:

(gdb) x/s *((char **)environ + 7)
0xbffffdef: "SHELLCODE=33Y100[=12=]413C1Ҳ7̀0[=12=]1K̀00777Hello, world!\n\r"

不幸的是,程序无法打印预期的问候语:

Program received signal SIGSEGV, Segmentation fault.
0xbffffdfd in ?? ()

为什么会崩溃,如何解决?

备注:

gcc (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0
GNU gdb (Ubuntu 8.1-0ubuntu3.2) 8.1.0.20180409-git
// program was compiled with the following flags:
-m32 -fno-stack-protector -z execstack -fno-PIE -no-pie -g

节目来源:

感谢@Nate Eldredge

此特定崩溃背后的原因是不正确的 return 地址 0xbffffdef。环境变量SHELLCODE的位置其实指向字符串的开头"SHELLCODE=..."

(gdb) x/s *((char **)environ + 7)
0xbffffdef: "SHELLCODE=33Y100[=10=]413C1Ҳ7̀0[=10=]1K̀00777Hello, world!\n\r"

为了解决这个问题,我们必须通过跳过前 10 个字符并指向 "33Y10...":

来调整地址
0xbffffdef + 0xa = 0xbffffdf9

当用这个值 0xbffffdf9 覆盖存储的 return 地址时,程序被强制进入问候世界:

(gdb) info frame
Stack level 0, frame at 0xbffff550:
 eip = 0x8048831 in main (6.c:52); saved eip = 0xbffffdf9
 source language c.
 Arglist at 0xbffff538, args: argc=6, argv=0xbffff5e4
 Locals at 0xbffff538, Previous frame's sp is 0xbffff550
 Saved registers:
  ebx at 0xbffff534, ebp at 0xbffff538, eip at 0xbffff54c
(gdb) cont
Continuing.
Hello, world!
[Inferior 1 (process 28591) exited normally]