Вопрос или проблема
Я написал чат-клиент на Python, который использует TLS-шифрование. Сервер работает на одном ПК, а клиент на другом.
Сервер создает SSL-ключи (server.key и server.crt), а затем я копирую server.crt на клиентский ПК.
Клиент не создает свои собственные SSL-ключи.
Используя LD_PRELOAD, я собираю SSLKEYLOGFILE на серверном ПК.
Я также собираю pcap, используя tcpdump на серверном ПК.
Я беру их оба в Wireshark для дешифровки pcap.
Однако только пакеты Client -> Server декодируются. Сообщения Server -> Client остаются зашифрованными. Почему так?
Keylog.txt:
SERVER_HANDSHAKE_TRAFFIC_SECRET 6e1c671e89c253c9670297d7af1c651236cb52ffcec31f393ff2d4c345b65b83 83156d3d139ab2bda9fb30bc68699fadeaff736373585e9296618973b804e67b858f904b6d67d35791f154d2df1c53ec
CLIENT_HANDSHAKE_TRAFFIC_SECRET 6e1c671e89c253c9670297d7af1c651236cb52ffcec31f393ff2d4c345b65b83 d27b9286b3f209da0cfca1055cd6c5a0b7dc638a3b47b760fc52c46530c6f0129e3ab8cb97de02d708dcd78e4b8eeef6
EXPORTER_SECRET 6e1c671e89c253c9670297d7af1c651236cb52ffcec31f393ff2d4c345b65b83 a81094854b39ab0a39ab4b1d0669591024a3c05d4a8b0df0870e2df824b447b9cdd206e4f120dbeb871a0f642bff783b
SERVER_TRAFFIC_SECRET_0 6e1c671e89c253c9670297d7af1c651236cb52ffcec31f393ff2d4c345b65b83 fa8a418607881a3c78082009acded37a4f1640b6d7e4932785b071bd3dcae67aaef91ef664bb1fc1f01e22d800b11e73
CLIENT_TRAFFIC_SECRET_0 6e1c671e89c253c9670297d7af1c651236cb52ffcec31f393ff2d4c345b65b83 f90a697b55859c7144ccf34c499869cddbec964f37386ab08cf7ed137cd54c53b9c119b42fda4f37b0ba3e5a62694cf7
Server.py:
import socket
import ssl
import argparse
# Серверная сторона
def create_tls_server(certfile, keyfile, port):
context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
context.load_cert_chain(certfile, keyfile)
hostname = socket.gethostname()
IPAddr = socket.gethostbyname(hostname)
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
sock.bind((IPAddr, port))
sock.listen(2)
print(f"Сервер запущен на {IPAddr}:{port}. Ожидание подключений...")
while True:
client_socket, addr = sock.accept() # Принимаем незащищенное соединение
try:
ssl_conn = context.wrap_socket(client_socket, server_side=True) # Оборачиваем здесь
print(f"Подключено {addr}. Ожидание сообщения...")
while True:
data = ssl_conn.recv(1024).decode()
if not data:
break
print(f"Получено: {data}")
if data == 'bye':
break
print(">> ", end='')
response = input()
ssl_conn.send(response.encode())
except Exception as e:
print(f"Ошибка: {e}")
finally:
ssl_conn.close()
client_socket.close() # закрываем соединение
print(f"Соединение с {addr} закрыто.")
break
if __name__ == '__main__':
parser = argparse.ArgumentParser(description="TLS Server")
parser.add_argument("-p", "--port", type=int, default=8500, help="Номер порта")
parser.add_argument("-v", "--version", type=int, default=ssl.PROTOCOL_TLS_SERVER, help="Версия TLS")
args = parser.parse_args()
create_tls_server('./server.crt', './server.key', args.port)
Client.py:
import socket
import ssl
import argparse
# Клиентская сторона
def connect_tls_client(cafile, port, host):
context = ssl.create_default_context()
context.load_verify_locations(cafile)
sock = socket.socket() # создаем сокет
sock.connect((host, port)) # подключаемся к серверу
client_socket = context.wrap_socket(sock, server_hostname=host)
print(">> ", end='')
message = input()
while message.lower().strip() != 'bye':
client_socket.send(message.encode()) # отправляем сообщение
data = client_socket.recv(1024).decode() # получаем ответ
if not data:
break
print('Получено от сервера: ' + data) # выводим на терминал
if data == 'bye':
break
print(">> ", end='')
message = input()
client_socket.close() # закрываем соединение
if __name__ == '__main__':
parser = argparse.ArgumentParser(description="TLS Client")
parser.add_argument("-s", "--server_ip", default="name-of-remote-serverenter image description here", help="IP адрес сервера")
parser.add_argument("-p", "--port", type=int, default=8500, help="Номер порта")
parser.add_argument("-v", "--version", type=int, default=ssl.PROTOCOL_TLS_SERVER, help="Версия TLS")
args = parser.parse_args()
connect_tls_client('./server.crt', args.port, args.server_ip)
Вот как это было решено для меня.
Я сделал 2 вещи:
- Разрешил поддисектору восстанавливать TCP-потоки.
Следующие предпочтения протокола TCP также необходимы для активации дешифрования TLS:
Разрешить поддисектору восстанавливать TCP-потоки. Включено по умолчанию.
Восстановление сегментов вне порядка (с Wireshark 3.0, отключено по умолчанию).
Ссылка
https://gitlab.com/wireshark/wireshark/-/issues/16713
https://wiki.wireshark.org/TLS#%E2%80%8DPreference_Settings
- Кроме того, моя первоначальная ошибка – я захватывал пакеты на конкретном порту, к которому сервер привязан, но, конечно, порт клиента был другим (и динамическим!), так что захват даже не происходил!
Я изменил свой запрос tcpcap для сбора только TLS-пакетов – это уменьшило шум в моих захватах и позволило захватить оба направления:
tcpdump -i any 'tcp[((tcp[12:1] & 0xf0) >> 2):1] & 0x80 = 0 && (tcp[((tcp[12:1] & 0xf0) >> 2):1] >= 0x14 && tcp[((tcp[12:1] & 0xf0) >> 2):1] <= 0x17)' -w tls_any_port.pcap
Сработало отлично!
Спасибо всем.
Ответ или решение
Когда вы сталкиваетесь с проблемой расшифровки пакетов с помощью Wireshark, где данные расшифровываются только в одном направлении, это может вызывать разочарование. Такая ситуация часто возникает в результате неправильной настройки или ошибки при сборе данных. Рассмотрим подробно возможные причины и пути их решения, используя указанный пример с клиент-серверным приложением на Python, работающим через TLS.
Теория
Когда вы используете протокол TLS для шифрования трафика, данные пересылаемые между клиентом и сервером шифруются с использованием симметричных и асимметричных ключей. Wireshark способен расшифровывать этот трафик, если у вас есть доступ к ключевым материалам, таким как SSLKEYLOGFILE. Даже когда все ключи собраны правильно, важно удостовериться, что все требования для расшифровки выполняются: использование корректных версий протоколов и спецификаций TCP/IP.
Основные причины того, что пакеты расшифровываются только в одном направлении (например, только от клиента к серверу), могут включать:
-
Отсутствие ключей для одной из сторон: Каждая сторона имеет свои собственные секреты трафика, которые нужны для расшифровки сообщения, и если ключи для сервера отсутствуют, записи в направлении "сервер-клиент" останутся зашифрованными.
-
Неправильный сбор сетевого трафика: Использование не того интерфейса, фильтрация трафика только по определенному порту или другой аспект конфигурации tcpdump может привести к тому, что пакеты от другой стороны просто не захватываются.
-
Настройки Wireshark: Отсутствие конфигурации собирающих подпроцессоров (subdissectors) для повторного объединения TCP сегментов может нарушить правильную расшифровку штрихов, так как некоторые пакеты в потоке могут быть пропущены или собраны неправильно.
Пример
Рассмотрим ваш пример:
- Серверная часть написана на Python и использует
ssl.SSLContext
для обработки TLS соединений, создавая ключиserver.key
иserver.crt
. - Клиентская часть использует
ssl.create_default_context()
для установления защищенного соединения с сервером. - Вы используете
LD_PRELOAD
сSSLKEYLOGFILE
на серверной машине для сбора ключей шифрования.
Логика сборки и использования SSL ключей описана корректно, однако, существует несколько важных аспектов, на которые стоит обратить внимание:
Применение
-
Проверка ключей: Убедитесь, что файл с ключами SSLKEYLOGFILE содержит как клиентские, так и серверные секреты трафика. Чтобы проверить, следует обратить внимание на наличие обеих записей
CLIENT_TRAFFIC_SECRET
иSERVER_TRAFFIC_SECRET
в лог-файле. В предоставленном вами лог-файле они присутствуют, поэтому это не является проблемой. -
Захват трафика: Используйте
tcpdump
без привязки к конкретному порту; в вашем решении вы уже использовали фильтр для захвата конкретных пакетов TLS, что было эффективным шагом. Сбор всех пакетов, а затем их фильтрация с помощью фильтров отображения Wireshark, такая какtls
, может упростить обнаружение проблемы, если захват не корректен. Это позволило вашему решению сработать корректно. -
Настройка Wireshark: Основной исправленный момент — это настройка Wireshark, чтобы он разрешал собирателям объединять TCP потоки, а также "reassemble out-of-order segments". Это критически важно для корректной обработки зашифрованных протоколов. Данные настройки находятся в разделе "Edit" -> "Preferences" -> "Protocols" -> "TCP", и от них зависит, как шифрованные данные будут приняты Wireshark.
Корректировка этих трех аспектов зачастую обеспечивает успешное расшифрование TLS-трафика в обоих направлениях. Предоставленный вами пример и обнаруженные решения показывают, что правильная конфигурация и проверка инструментов сбора и анализа данных является ключевым фактором. Такое доскональное внимание к деталям поможет не только в текущем, но и в возможных будущих сетевых исследованиях.