我正在遵循opensecuritytraining课程“漏洞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文件,然后在shellcode之前将上述perl文件的输出以及39条nop指令重定向到一个名为“ shellcode”的文件,因此有效载荷现在为64个字节长,填充缓冲区。然后,我在gdb中打开了c程序,并在nop底座中间选择了一个地址,它将是新的返回地址(0xbffff540)。我将地址附加到“ shellcode”文件中,并附加了4个字节以覆盖保存的帧指针。 shellcode看起来像这样:
现在,当我尝试在c程序中的gdb中运行此shellcode时,它将在地址0xbffff575处引起分段错误,该错误指向我的shellcode中的某个点0x62,它是“ / bin”中的字符“ b” / sh”。是什么原因造成的?
这是我的堆栈框架,确认我选择的返回地址确实返回到nop底座的中间。
main
返回到您的shellcode后,ESP可能会指向该缓冲区的正上方。 EIP指向了它的开始。这就是返回的含义。
[一对push
指令可能会在缓冲区末尾修改机器代码,导致SIGILL的EIP指向您刚刚压入的字节。
[最简单的解决方法可能是add esp, -128
一直超过缓冲区。或sub esp, -128
在堆栈中更高。 (-128
是您可以使用的最大8位立即数,避免在sub esp, 128
或1024
的机器代码中引入零。如果想将堆栈移得更远,您当然可以在一个寄存器。)
[[我没有测试这个猜测,但是您可以在GDB中通过从si
的末尾使用main
单步进入您的shellcode并逐步执行指令来确认它。在每条指令后使用disas
进行拆卸。或使用layout reg
。有关更多GDB调试技巧,请参见https://stackoverflow.com/tags/x86/info的底部。
argv
数组,而不是仅为char **argv
和char **envp
传递NULL指针。 (在Linux上,这与指向以NULL终止的空数组的有效指针相同:http://man7.org/linux/man-pages/man2/execve.2.html#NOTES。)>但是主要区别在于,它使用jmp / call / pop来获取指向内存中已经存在的字符串的指针。
这只是一个堆栈插槽,而不是三个
。 (返回地址之前的有效载荷的结尾是数据,而不是指令,但是如果执行过多的推入操作和覆盖字符串而不是仅存储0
终止符,则它将以不同的方式失败。call
向后跳转之前推送的返回地址实际上修改了缓冲区,但是如果它确实覆盖了结尾附近的任何内容,它仍然会中断。)这只是第三次推动没有任何进展
。这是有道理的:前两个大概覆盖了包含新的返回地址和保存的EBP值的有效负载部分。碰巧的是,编译器将main
的缓冲区与此邻接。如果您实际使用的是tcc
而不是gcc
,那可能就不足为奇了。 GCC会将其对齐16,并且可能出于某种原因或其他原因在缓冲区和堆栈帧的顶部之间留有间隙。