
Une manière un peu moins hackish d'intercepter et de modifier les protocoles non-HTTP via Burp et autres.
Une façon un peu moins bidouillée d'intercepter et modifier des protocoles non-HTTP via Burp et autres, avec prise en charge de l'interception SSL et TLS. Cet outil est destiné aux chercheurs et testeurs d'intrusion applicative qui réalisent des audits de sécurité de clients lourds.
Une version améliorée du fantastique projet mitm_relay.
Dans le cadre de notre travail au sein du département de recherche de CyberArk Labs, nous avions besoin d'un moyen d'inspecter les communications SSL et TLS sur TCP et de pouvoir modifier le contenu des paquets à la volée. Il existe de nombreuses façons de le faire (par exemple, l'extension Burp Suite bien connue NoPE), mais aucune ne fonctionnait pour nous dans certains cas. Finalement, nous sommes tombés sur mitm_relay.
mitm_relay est un moyen rapide et simple de réaliser une MITM de tout protocole basé sur TCP via des logiciels d'interception HTTP existants, comme le proxy de Burp Suite. C'est particulièrement utile pour les audits de sécurité de clients lourds. Mais cela ne fonctionnait pas complètement pour nous, donc nous avons dû le personnaliser. Après de nombreuses personnalisations, chaque nouveau changement nécessitait beaucoup de travail, et nous avons fini par tout réécrire de manière plus modulaire.
Nous espérons que d'autres trouveront ce script utile, et nous espérons que l'ajout de fonctionnalités sera facile.
Pour commencer, il faut configurer les adresses et ports des écouteurs. Pour chaque écouteur, une cible doit également être configurée (adresse et port). Chaque donnée reçue de l'écouteur sera encapsulée dans le corps d'une requête HTTP POST avec l'URL contenant « CLIENT_REQUEST ». Chaque donnée reçue de la cible sera encapsulée dans le corps d'une requête HTTP POST avec l'URL contenant « SERVER_RESPONSE ». Ces requêtes sont envoyées à un serveur d'interception HTTP local.
Il est possible de configurer un proxy HTTP et d'utiliser un outil comme burp suite comme outil d'interception HTTP pour visualiser les messages. Ainsi, il est facile de modifier les messages en utilisant les fonctions « Match and Replace » de Burp, des extensions, ou même manuellement (rappelez-vous, le mécanisme de temporisation du protocole intercepté peut être très court).
Une autre façon de modifier les messages est d'utiliser un script Python que le serveur d'interception HTTP exécutera lorsqu'il recevra des messages.
Le corps des messages envoyés au serveur d'interception HTTP sera affiché dans le terminal. Les messages seront affichés après les modifications si un script de modification est fourni. Après toutes les modifications, le serveur d'interception renverra également le corps de la réponse HTTP.
Pour déchiffrer la communication SSL/TLS, mitm_intercept doit recevoir un certificat et une clé que le client acceptera lors de l'établissement de la liaison avec l'écouteur. Si le serveur cible nécessite un certificat spécifique pour l'établissement de la liaison, il est possible de fournir un certificat et une clé.
Un petit schéma pour illustrer le flux de trafic typique :

mitm_intercept est compatible avec les versions récentes de Python 3 (Python 3.9) et est également compatible avec Windows (socket.MSG_DONTWAIT n'existe pas sous Windows, par exemple). Nous avons conservé l'option d'utiliser « STARTTLS », que nous avons appelée mode « Mixte ». L'utilisation du fichier de journalisation des clés SSL est mise à jour (l'option intégrée pour l'utiliser est nouvelle depuis Python 3.8), et nous avons ajouté l'option de modifier l'en-tête SNI. Désormais, la gestion des communications entrantes et sortantes est effectuée par socketserver, et toutes les données sont envoyées à une sous-classe de ThreadingHTTPServer qui gère la représentation et la modification des données. Ainsi, il est possible de voir les modifications appliquées par le script de modification dans la réponse (pratique pour utiliser Burp). De plus, nous pouvons désormais modifier les chiffrements disponibles utilisés par le script en utilisant le format de liste de chiffrements 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.
La communication doit être dirigée vers l'écouteur pour intercepter des protocoles arbitraires. La manière de procéder dépend du fonctionnement du client. Parfois, il utilise une adresse DNS, et modifier le fichier hosts suffira pour résoudre l'adresse de l'écouteur. Si l'adresse est codée en dur, des méthodes plus créatives doivent être appliquées (généralement des modifications de la table de routage, le patching du client, ou l'utilisation d'une VM et d'iptables).
Le serveur d'interception HTTP peut exécuter un script qui lui est fourni avec le flag -s. Ce script s'exécute lorsque les requêtes HTTP sont reçues. La réponse du serveur d'interception HTTP est la requête reçue après exécution du script.
Lorsqu'un proxy est configuré (comme Burp), les modifications de la requête auront lieu avant l'exécution du script, et les modifications de la réponse après. Les altérations de la requête et de la réponse par le proxy ou le script de modification changeront le message d'origine avant qu'il ne parvienne à la destination.
Le script doit contenir les fonctions handle_request(message) et handle_response(message). Le serveur d'interception HTTP appellera handle_request(message) lorsque le message provient du client vers le serveur, et handle_response(message) lorsque le message provient du serveur vers le client.
Un exemple de script qui ajoute un octet nul à la fin du message :
def handle_request(message):
return message + b"\x00"
def handle_response(message):
# Les deux fonctions doivent retourner un message.
return message
L'outil nécessite un certificat serveur et une clé privée pour l'interception SSL. Des informations sur la génération d'un certificat auto-signé ou du certificat de Burp peuvent être trouvées ici.
Si le serveur nécessite un certificat spécifique, un certificat et une clé peuvent être fournis à l'outil.
La démo ci-dessous montre comment intercepter une connexion avec MSSQL (cette démo a été réalisée sur DVTA):
La connexion à MSSQL se fait via le protocole TDS sur TCP. L'authentification elle-même est effectuée avec TLS sur le protocole TDS. Pour intercepter ce processus TLS, nous aurons besoin de deux scripts de modification un peu bricolés.
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:]
Nous verrons une partie de la communication TLS avec ces scripts bricolés, mais ensuite le client échouera (car avec ces scripts bidouillés, nous modifions mal la communication TDS sauf la partie TLS).
Copyright (c) 2022 CyberArk Software Ltd. Tous droits réservés
Ce dépôt est sous licence Apache-2.0 - voir LICENSE pour plus de détails.