无法在漏洞利用期间注入有效的指令指针

Unable to inject valid instruction pointer during exploit

我正在尝试利用给定的程序,但我不知道自己做错了什么。长话短说,我设法注入代码来覆盖 RIP。这意味着我应该能够重定向代码执行,但问题是,我得到了 SIGSEGV。我是否必须以特殊方式设计注入堆栈才能不获取 SIGSEGV?

我的游戏计划是利用函数 mainloop 并更改 return 地址。函数 mainloop 的堆栈具有以下值:

0000| 0x7fffffffdff0 --> 0xa7400ffffe010 
0008| 0x7fffffffdff8 --> 0xf423f55758260 
0016| 0x7fffffffe000 --> 0x7fffffffe010 --> 0x5555555550b0 (<__libc_csu_init>:  push   r15)
0024| 0x7fffffffe008 --> 0x5555555550a4 (<main+66>: mov    eax,0x0)

所以 return 地址存储在 0x7fffffffe008 并且我已经设法用指向我要执行的代码的地址覆盖该值。在这种情况下,地址 0x555555554e6e.

程序回溯如下:

#6  0x0000555555554fab in mainloop ()
#7  0x00005555555550a4 in main ()
#8  0x00007ffff7e1109b in __libc_start_main (main=0x555555555062 <main>, argc=0x1, argv=0x7fffffffe0f8, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, 
stack_end=0x7fffffffe0e8) at ../csu/libc-start.c:308
#9  0x000055555555486a in _start () 

如你所见,当我退出 mainloop 时,我将 return 编辑到 main,而当我退出 main 时,我会转到一堆 libc 函数,以便程序干净地退出 (?)。

那么当我 运行 我的漏洞利用代码时会发生什么?这个:

0000| 0x7ffe1b3d4150 --> 0x424142001b3d4170 
0008| 0x7ffe1b3d4158 ("ABABABABABABABABnNUUUU")
0016| 0x7ffe1b3d4160 ("ABABABABnNUUUU")
0024| 0x7ffe1b3d4168 --> 0x555555554e6e ('nNUUUU')
0032| 0x7ffe1b3d4170 --> 0x55a34784000a 
0040| 0x7ffe1b3d4178 --> 0x7f7c156c409b (<__libc_start_main+235>:      mov    edi,eax)
0048| 0x7ffe1b3d4180 --> 0x0 
0056| 0x7ffe1b3d4188 --> 0x7ffe1b3d4258 --> 0x7ffe1b3d5474 ("./device")

您看到的是堆栈。我添加了一些字节,以便您可以获得更多上下文。但我想我设法为我的漏洞找到了正确大小的填充物。我已设法更改字节 24 处的值。

但是我的PEDA/GDB好像没有把那个值当成指令指针,这很奇怪。它似乎将其视为C字符串(?)。回溯看起来像这样:

#6  0x000055a347849fab in mainloop ()
#7  0x0000555555554e6e in ?? ()
#8  0x000055a34784000a in ?? ()
#9  0x00007f7c156c409b in __libc_start_main (main=0x55a34784a062 <main>, argc=0x1, argv=0x7ffe1b3d4258, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, 
stack_end=0x7ffe1b3d4248) at ../csu/libc-start.c:308
#10 0x000055a34784986a in _start () 

当我退出 mainloop 时,我在 PEDA/GDB 中得到以下信息:

Stopped reason: SIGSEGV
0x0000555555554e6e in ?? ()

如果我在 GDB 中 运行 命令 i f 我得到:

Stack level 0, frame at 0x7ffe1b3d4178:
 rip = 0x555555554e6e; saved rip = 0x55a34784000a
 called by frame at 0x7ffe1b3d4180
 Arglist at 0x7ffe1b3d4168, args: 
  Locals at 0x7ffe1b3d4168, Previous frame's sp is 0x7ffe1b3d4178
 Saved registers:
  rip at 0x7ffe1b3d4170 

在地址 0x0000555555554e6e 处,程序执行以下 ASM:

0x0000555555554e6e <+172>:  lea    rdi,[rip+0x20126b]        #    0x5555557560e0 <flag2>

所以我似乎拥有正确的 RIP,但仅此而已。 伙计们,怎么回事?

当您从 GDB 中启动程序时,GDB(默认情况下)会禁用 ASLR。在 PIE 可执行文件中,静态 code/data 地址是随机的。注入 return 地址只有在您知道正确的绝对地址时才有效。

但显然您有时会在 GDB 之外启动您的程序,其中的地址每次都不相同。这显然会导致段错误,并且您的问题没有显示在发生段错误的实际过程中与实际 0x0000555555554e6e 的反汇编,这与您声称/假设的相反。

(GCC 在默认情况下制作 PIE 可执行文件是 Linux 上最近的事情;如果您正在学习旧教程,它可能假设可执行文件本身是位置相关的,只有库 + 堆栈会成为 ASLRed。32-bit absolute addresses no longer allowed in x86-64 Linux?)

有关禁用 ASLR 或构建非 PIE 可执行文件的系统范围或每个进程的方法,请参阅 Disable randomization of memory addresses