尝试将“/bin//sh”推送到堆栈时,32 位 shellcode 会导致分段错误

32 bit shellcode causes a segmentation fault when trying to push "/bin//sh" to the stack

我正在学习开放式安全培训课程 "exploits 1"。目前,我正在尝试使用缓冲区溢出在 32 位 linux 系统上利用带有一些 shell 代码的简单 c 程序。 c程序:

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

我使用命令 "tcc -g -o basic_vuln basic_vuln.c" 编译了程序。然后,我编写了以下 shell 代码。

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 文件的输出连同 shell 代码之前的 39 条 nop 指令重定向到一个名为 "shellcode" 的文件,所以负载现在是 64 字节长,填满了缓冲区。然后,我在 gdb 中打开 c 程序,并在 nop sled 中间选择一个地址,这将是新的 return 地址(0xbffff540)。我将地址附加到 "shellcode" 文件,连同 4 个字节以覆盖保存的帧指针。 shell代码如下所示:

现在,当我尝试 运行 在 c 程序的 gdb 中编写此 shell 代码时,它会在地址 0xbffff575 处导致分段错误,它指向我的 [=43] 中的某个点=]code,0x62,即“/bin/sh”中的字符"b"。这可能是什么原因造成的?

这是我的堆栈框架,确认我选择的 return 地址确实 return 到了 nop sled 的中间。

课程确实提供了 shell在 c 程序中的 gdb 中工作的代码:

main return 进入你的 shellcode 之后,ESP 可能会指向那个缓冲区的正上方。 EIP指向它的开始; return就是这个意思。

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

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

我没有测试这个猜测,但是你可以在 GDB 中通过从 main[=55 的末尾使用 si 单步进入你的 shellcode 来确认它=] 按照说明进行操作。

在每条指令后使用disas查看反汇编。或者使用 layout reg。请参阅 https://whosebug.com/tags/x86/info 的底部以获取更多 GDB 调试技巧。


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

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


@Margaret 对此进行了更详细的调查,并发现 它只是第 3 次推送破坏了任何东西。这是有道理的:前两个可能会覆盖包含新 return 地址和保存的 EBP 值的负载部分。碰巧编译器将 main 的缓冲区与它相邻。

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