【问题标题】:32 bit shellcode causes a segmentation fault when trying to push "/bin//sh" to the stack尝试将“/bin//sh”推送到堆栈时,32 位 shellcode 会导致分段错误
【发布时间】:2020-07-17 10:48:48
【问题描述】:

我正在学习 opensecuritytraining 课程“exploits 1”。目前,我正在尝试使用缓冲区溢出在 32 位 linux 系统上利用带有一些 shellcode 的简单 c 程序。 c程序:

void main(int argc, char **argv)
{
    char buf[64];
    strcpy(buf,argv[1]);
}

我使用命令“tcc -g -o basic_vuln basic_vuln.c”编译了程序。然后,我编写了以下 shellcode。

section .text

global _start

_start:
xor eax, eax
xor ebx, ebx
xor ecx, ecx
xor edx, edx

mov al, 11

push ebx
push 0x68732f2f
push 0x6e69622f
mov ebx, esp

int 0x80

我通过键入“nasm -f elf shell.asm; ld -o shell shell.o”来编译它。当我尝试自己执行“shell”时,它可以工作并且我得到一个shell。接下来,我用 objdump 反汇编程序,编写了一个打印操作码的 perl 文件,然后将所述 perl 文件的输出以及 shellcode 之前的 39 个 nop 指令重定向到一个名为“shellcode”的文件,因此有效负载现在是 64 字节长,填充缓冲区。然后,我在 gdb 中打开 c 程序,并在 nop sled 中间选择一个地址,这将是新的返回地址(0xbffff540)。我将地址附加到“shellcode”文件中,并附加了 4 个字节来覆盖保存的帧指针。 shellcode 如下所示:

现在,当我尝试在 c 程序的 gdb 中运行这个 shellcode 时,它​​会导致地址 0xbffff575 处出现分段错误,该地址指向我的 shellcode 中的某个点 0x62,即“/”中的字符“b”垃圾箱/sh”。这是什么原因造成的?

这是我的堆栈帧,确认我选择的返回地址确实返回到 nop sled 的中间。

该课程确实提供了可以在 c 程序中的 gdb 中工作的 shellcode:

【问题讨论】:

  • 您的 C 程序是否编译为 64 位程序?或者你只是为你的漏洞选择了错误的返回地址,所以你返回到你的 shellcode 的中间,EIP 指向立即数的一个字节?在它崩溃之前使用调试器单步执行,看看会发生什么。在您正在攻击的函数中的ret 上设置断点。也许你的 NOP 幻灯片不够长。或者你在把它变成 shellcode 的时候打错了一些东西。你只有你的文字图片,所以我不能复制/粘贴它和 unhexdump + 反汇编,即使我想。你可以使用ndisasm -b32
  • 您可能已经针对这种漏洞利用了系统缓解措施:在当前的 Linux 系统上,无法从包含堆栈的内存区域执行机器指令。跳入该区域会立即导致段错误。您的课程是否提到过这种可能性?
  • @PeterCordes 我在 opensecuritytraining 提供的虚拟机中运行它,所以复制粘贴有点困难。我在 shellcode 上运行了 ndisasm -b32,这是程序返回的内容:imgur.com/a/DULAnzz
  • 那是什么鬼?你的指令有问题吗?我认为您只是将屏幕截图做得不好,因为指令地址也不连续。无论如何,使用 GDB 单步进入漏洞利用有效负载并查看内存中实际内容的反汇编。
  • 我会尝试从 strcpy 返回的点开始单步执行程序(在 gdb 中使用 si 一次执行一条指令)。如果您还没有熟悉 gdb,现在是时候熟悉一下了。

标签: c linux security assembly exploit


【解决方案1】:

main 返回到您的 shellcode 后,ESP 可能会指向该缓冲区上方。 EIP 指向它的开始;这就是回归的意义。

一对push 指令可能会修改缓冲区末尾的机器代码,从而导致带有 EIP 的 SIGILL 指向您刚刚推送的字节。

可能最简单的解决方法是 add esp, -128 一直越过缓冲区。或sub esp, -128 可以在堆栈中更高。 (-128 是您可以使用的最大幅度的 8 位立即数,避免在使用 sub esp, 1281024 的机器代码中引入零。如果您想将堆栈移动得更远,您当然可以在一个寄存器。)

我没有测试这个猜测,但是您可以在 GDB 中通过在 main 末尾使用 si 单步进入您的 shellcode 以逐步说明来确认它。 p>

在每条指令后使用disas 来查看反汇编。或使用layout reg。更多 GDB 调试技巧见底部https://stackoverflow.com/tags/x86/info


给定的解决方案更复杂,因为它显然设置了一个实际的argv 数组,而不是仅仅为char **argvchar **envp 传递NULL 指针。 (在 Linux 上,这与指向空 NULL 终止数组的有效指针相同:http://man7.org/linux/man-pages/man2/execve.2.html#NOTES)。

但关键区别在于它使用 jmp/call/pop 来获取指向已在内存中的字符串的指针。 这只是一个堆栈插槽而不是三个。 (它在返回地址之前的有效载荷的结尾是数据,而不是指令,但是如果它执行太多推送并覆盖字符串而不是仅仅存储0 终止符,它将以不同的方式失败。call 向后跳转之前推送的返回地址实际上修改了缓冲区,但如果它确实覆盖了接近末尾的任何内容,它仍然会中断。)


@Margaret 对此进行了更详细的研究,并发现 只有第三次推动才能破坏任何东西。这是有道理的:前 2 个可能会覆盖包含新返回地址和保存的 EBP 值的有效负载部分。碰巧编译器将main的缓冲区与其相邻。

如果您实际上使用的是tcc 而不是gcc,那可能并不奇怪。 GCC 会将其对齐 16,并且可能出于某种原因在缓冲区和堆栈帧的顶部之间留下了间隙。

【讨论】:

  • 值得注意的是,OP错误地识别出指令错误:报告的地址是0xbffff575,根据发布的转储,地址是int 80h。事实上,(OP 使用的 VM 必须禁用 ASLR)我们可以看到 main 返回时 esp 位于 0xbffff57c。因此,当 shellcode 执行时,esp 是 0xbffff580` 并且 third 推送会覆盖 0xbffff574 处的代码。
  • @MargaretBloom:哦,很好看。在写这个答案时,我只考虑了两个 push-immediates,忘记了 push reg。已修复,它不是 3,而不是 2。包括你的一些侦探工作和基于此的结论,谢谢。回复:ASLR:如果你只在 GDB 中运行,它会默认禁用 ASLR(包括我认为的堆栈)。但是,是的,VM 可能通过 /proc 设置全局禁用了 ASLR。
猜你喜欢
  • 1970-01-01
  • 2020-07-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-04-16
相关资源
最近更新 更多