
Немного менее хакерский способ перехватывать и модифицировать не-HTTP протоколы через Burp и другие.
Немного менее хакерский способ перехвата и модификации не-HTTP протоколов через Burp и другие инструменты с поддержкой перехвата SSL и TLS. Этот инструмент предназначен для исследователей и пентестеров приложений, проводящих оценку безопасности толстых клиентов.
Улучшенная версия замечательного проекта mitm_relay.
В рамках нашей работы в исследовательском отделе CyberArk Labs нам понадобился способ инспектировать SSL/TLS коммуникации через TCP и иметь возможность изменять содержимое пакетов на лету. Существует множество способов (например, известное расширение Burp Suite NoPE), но в некоторых случаях ни один из них нам не подходил. В итоге мы наткнулись на mitm_relay.
mitm_relay — это быстрый и простой способ выполнять MITM любого TCP-протокола через существующее программное обеспечение для перехвата HTTP, такое как прокси Burp Suite. Это особенно полезно при оценке безопасности толстых клиентов. Но он не полностью работал у нас, поэтому мы решили его доработать. После множества изменений каждое новое усовершенствование требовало много работы, и в итоге мы переписали всё в более модульном виде.
Мы надеемся, что этот скрипт окажется полезным для других, и что добавление функционала будет простым.
Для начала необходимо настроить адреса и порты слушателей. Для каждого слушателя также нужно настроить цель (адрес и порт). Все данные, полученные от слушателя, будут обёрнуты в тело HTTP POST-запроса с URL, содержащим "CLIENT_REQUEST". Все данные, полученные от цели, будут обёрнуты в тело HTTP POST-запроса с URL, содержащим "SERVER_RESPONSE". Эти запросы отправляются на локальный HTTP-сервер перехвата.
Есть возможность настроить HTTP-прокси и использовать инструмент вроде Burp Suite в качестве средства перехвата HTTP и просматривать сообщения там. Таким образом, легко изменять сообщения с помощью функций Burp "Match and Replace", расширений или даже вручную (помните, что таймаут механизма перехватываемого протокола может быть очень коротким).
Другой способ изменения сообщений — использование Python-скрипта, который HTTP-сервер перехвата будет выполнять при получении сообщений.
Тело сообщений, отправляемых на HTTP-сервер перехвата, будет выведено на консоль. Если задан скрипт модификации, сообщения будут выведены после изменений. После всех модификаций сервер перехвата также вернёт их в виде тела HTTP-ответа.
Для расшифровки SSL/TLS трафика mitm_intercept необходимо предоставить сертификат и ключ, которые клиент примет при установке рукопожатия со слушателем. Если целевой сервер требует определённый сертификат для рукопожатия, есть возможность указать сертификат и ключ.
Небольшая схема типичного потока трафика:

mitm_intercept совместим с новыми версиями Python 3 (Python 3.9) и также совместим с Windows (например, socket.MSG_DONTWAIT отсутствует в Windows). Мы сохранили возможность использования "STARTTLS", назвав это режимом "Mixed". Использование файла журнала ключей SSL обновлено (встроенная возможность его использования появилась в Python 3.8), и мы добавили возможность изменять заголовок SNI. Теперь управление входящей и исходящей связью осуществляется через socketserver, а все данные отправляются в подкласс ThreadingHTTPServer, который отвечает за представление и модификацию данных. Благодаря этому можно видеть изменения, применённые скриптом модификации, в ответе (удобно для использования с Burp). Также теперь можно изменять доступные шифры, используемые скриптом, с помощью формата списка шифров OpenSSL.
$ python -m pip install requestsusage: mitm_intercept.py [-h] [-m] -l [u|t:]<interface>:<port> [[u|t:]<interface>:<port> ...] -t
[u|t:]<addr>:<port> [[u|t:]<addr>:<port> ...] [-lc <cert_path>]
[-lk <key_path>] [-tc <cert_path>] [-tk <key_path>] [-w <interface>:<port>]
[-p <addr>:<port>] [-s <script_path>] [--sni <server_name>]
[-tv <defualt|tls12|tls11|ssl3|tls1|ssl2>] [-ci <ciphers>]
mitm_intercept version 1.6
options:
-h, --help show this help message and exit
-m, --mix-connection Perform TCP relay without SSL handshake. If one of the relay sides starts an
SSL handshake, wrap the connection with SSL, and intercept the
communication. A listener certificate and private key must be provided.
-l [u|t:]<interface>:<port> [[u|t:]<interface>:<port> ...], --listen [u|t:]<interface>:<port> [[u|t:]<interface>:<port> ...]
Creates SSLInterceptServer listener that listens on the specified interface
and port. Can create multiple listeners with a space between the parameters.
Adding "u:" before the address will make the listener listen in UDP
protocol. TCP protocol is the default but adding "t:" for cleanliness is
possible. The number of listeners must match the number of targets. The i-th
listener will relay to the i-th target.
-t [u|t:]<addr>:<port> [[u|t:]<addr>:<port> ...], --target [u|t:]<addr>:<port> [[u|t:]<addr>:<port> ...]
Directs each SSLInterceptServer listener to forward the communication to a
target address and port. Can create multiple targets with a space between
the parameters. Adding "u:" before the address will make the target
communicate in UDP protocol.TCP protocol is the default but adding "t:" for
cleanliness is possible. The number of listeners must match the number of
targets. The i-th listener will relay to the i-th target.
-lc <cert_path>, --listener-cert <cert_path>
The certificate that the listener uses when a client contacts him. Can be a
self-sign certificate if the client will accept it.
-lk <key_path>, --listener-key <key_path>
The private key path for the listener certificate.
-tc <cert_path>, --target-cert <cert_path>
The certificate that used to create a connection with the target. Can be a
self-sign certificate if the target will accept it. Doesn't necessary if the
target doesn't require a specific certificate.
-tk <key_path>, --target-key <key_path>
The private key path for the target certificate.
-w <interface>:<port>, --webserver <interface>:<port>
Specifies the interface and the port the InterceptionServer webserver will
listens on. If omitted the default is 127.0.0.1:49999
-p <addr>:<port>, --proxy <addr>:<port>
Specifies the address and the port of a proxy between the InterceptionServer
webserver and the SSLInterceptServer. Can be configured so the communication
will go through a local proxy like Burp. If omitted, the communication will
be printed in the shell only.
-s <script_path>, --script <script_path>
A path to a script that the InterceptionServer webserver executes. Must
contain the function handle_request(message) that will run before sending it
to the target or handle_response(message) after receiving a message from the
target. Can be omitted if doesn't necessary.
--sni <server_name> If there is a need to change the server name in the SSL handshake with the
target. If omitted, it will be the server name from the handshake with the
listener.
-tv <defualt|tls12|tls11|ssl3|tls1|ssl2>, --tls-version <defualt|tls12|tls11|ssl3|tls1|ssl2>
If needed can be specified a specific TLS version.
-ci <ciphers>, --ciphers <ciphers>
Sets different ciphers than the python defaults for the TLS handshake. It
should be a string in the OpenSSL cipher list format
(https://www.openssl.org/docs/manmaster/man1/ciphers.html).
For dumping SSL (pre-)master secrets to a file, set the environment variable SSLKEYLOGFILE with a
file path. Useful for Wireshark.
Трафик необходимо направлять на слушатель для перехвата произвольных протоколов. Способ зависит от того, как работает клиент. Иногда он использует DNS-адрес, и изменения файла hosts достаточно для разрешения адреса слушателя. Если адрес жёстко закодирован, необходимо применить более творческие методы (обычно это модификация таблицы маршрутизации, патч клиента или использование виртуальной машины и iptables).
HTTP-сервер перехвата может выполнять скрипт, переданный с флагом -s. Этот скрипт запускается при получении HTTP-запросов. Ответ от HTTP-сервера перехвата — это полученный запрос после выполнения скрипта.
Если настроен прокси (например, Burp), изменения запроса произойдут до выполнения скрипта, а изменения ответа — после. Изменения запроса и ответа через прокси или скрипт модификации изменят исходное сообщение до его отправки к месту назначения.
Скрипт должен содержать функции handle_request(message) и handle_response(message). HTTP-сервер перехвата вызовет handle_request(message), когда сообщение идёт от клиента к серверу, и handle_response(message), когда сообщение идёт от сервера к клиенту.
Пример скрипта, добавляющего нулевой байт в конец сообщения:
def handle_request(message):
return message + b"\x00"
def handle_response(message):
# Обе функции должны возвращать сообщение.
return message
Инструменту требуется сертификат сервера и закрытый ключ для перехвата SSL. Информацию о создании самоподписанного сертификата или сертификата Burp можно найти здесь.
Если сервер требует определённый сертификат, можно предоставить сертификат и ключ инструменту.
На видео ниже показано, как перехватить соединение с MSSQL (демонстрация выполнена на DVTA):
Соединение с MSSQL осуществляется через протокол TDS поверх TCP. Аутентификация выполняется с помощью TLS поверх TDS. Чтобы увидеть перехват этого TLS-процесса, нам понадобятся два корявых скрипта модификации.
demo_script.py:
from time import time
from struct import pack
from pathlib import Path
def handle_request(message):
if message.startswith(b"\x17\x03"):
return message
with open("msg_req" + str(time()), "wb") as f:
f.write(message[:8])
return message[8:]
def handle_response(message):
if message.startswith(b"\x17\x03"):
return message
path = Path(".")
try:
msg_res = min(i for i in path.iterdir() if i.name.startswith("msg_res"))
data = msg_res.read_bytes()
msg_res.unlink()
except ValueError:
data = b'\x12\x01\x00\x00\x00\x00\x01\x00'
return data[:2] + pack(">h", len(message)+8) + data[4:] + message
demo_script2.py:
from time import time
from struct import pack
from pathlib import Path
def handle_request(message):
if message.startswith(b"\x17\x03"):
return message
path = Path(".")
try:
msg_req = min(i for i in path.iterdir() if i.name.startswith("msg_req"))
data = msg_req.read_bytes()
msg_req.unlink()
except ValueError:
data = b'\x12\x01\x00\x00\x00\x00\x01\x00'
return data[:2] + pack(">h", len(message)+8) + data[4:] + message
def handle_response(message):
if message.startswith(b"\x17\x03"):
return message
with open("msg_res" + str(time()), "wb") as f:
f.write(message[:8])
return message[8:]
С этими корявыми скриптами мы увидим часть TLS-трафика, но затем клиент потерпит неудачу (потому что эти скрипты плохо изменяют TDS-коммуникацию, кроме TLS-части).
Copyright (c) 2022 CyberArk Software Ltd. All rights reserved
Этот репозиторий лицензирован под лицензией Apache-2.0 — подробнее см. в LICENSE.