Mitmproxy
Mitmproxy est un mandataire (proxy) destiné au débogage, tests, notamment de pénétrations ainsi qu'aux évaluations des mesures de protections d'un système d'information.
Il est extrêmement utile pour comprendre le fonctionnement d'un programme officiant au travers de sessions HTTPS puisqu'il permet de réunir les éléments nécessaires à l'inspection des trames chiffrées par TLS.
Il peut, en outre, servir à espionner la navigation HTTPS des utilisateurs d'un réseau à accès limité (obligeant l'accès au WAN par l'intermédiaire d'un mandataire). À l'issue de la lecture de ce document, vous devriez être sensibilisé à ne jamais réaliser d'opérations personnelles sur ce genre de réseau (connexion à un Webmail ou à tout portail nécessitant un identifiant personnel par exemple).
Cette documentation explique brièvement comment se servir de l'outil pour enregistrer les clés de sessions TLS et présente un cas pratique avec le déchiffrement d'un docker pull
. Les trames capturées via Tcpdump pourront alors être examinées par un outil graphique comme Wireshark.
Mise en œuvre
Installation
Installation de l'outil
apt install --no-install-recommends mitmproxy
Le fait de lancer mitmproxy
créé automatiquement le répertoire ~/.mitmproxy
contenant les éléments secrets utilisés dans une transaction TLS. Il est alors possible d'importer le certificat serveur dans la base local du client à espionner ou générer son propre certificat via OpenSSL.
Afin d'agrémenter la démonstration, la seconde solution sera utilisée. Outre l'aspect didactique, générer votre propre certificat est indispensable lorsque l'interception concerne la communication d'une application exigeante en terme de vérification (ce pourquoi Docker est utilisé en guise d'exemple). Il est en effet nécessaire dans ce cas d'ajouter des noms alternatifs dans la section subjectAltName
du certificat X.509 afin de spécifier les adresses signées par celui-ci.
Configuration des clés
Création du répertoire d'accueil des éléments secrets
mkdir -p ~/.mitmproxy/certs
Génération desdits éléments
# Génération de la clé privée de l'autorité de certification (AC)
openssl genrsa -out ~/.mitmproxy/certs/ca.key 2048
# Génération du couple de clés du serveur mandataire
openssl req -new -x509 -days 365 -key ~/.mitmproxy/certs/ca.key -subj "/C=CN/ST=GD/L=SZ/O=Acme, Inc./CN=Acme Root CA" -out ~/.mitmproxy/certs/ca.crt
# Création d'un fichier de demande de certificat auprès de l'AC
openssl req -newkey rsa:2048 -nodes -keyout ~/.mitmproxy/certs/serveur.key -subj "/C=CN/ST=GD/L=SZ/O=Acme, Inc./CN=*" -out ~/.mitmproxy/certs/serveur.csr
# Génération d'un certificat signé par l'AC et intégrant la liste des domaines à certifier
openssl x509 -req -extfile <(printf "subjectAltName=DNS:*.ycharbi.fr,DNS:*.docker.io,DNS:*.cloudflare.docker.com,IP:127.0.0.1") -days 365 -in ~/.mitmproxy/certs/serveur.csr -CA ~/.mitmproxy/certs/ca.crt -CAkey ~/.mitmproxy/certs/ca.key -CAcreateserial -out ~/.mitmproxy/certs/serveur.crt
# Chaînage du certificat avec la clé associée pour mitmproxy
cat ~/.mitmproxy/certs/serveur.crt ~/.mitmproxy/certs/serveur.key > ~/.mitmproxy/certs/cert.pem
Le point d'intérêt principal de ces opérations et qui devra être personnalisé en fonction du cas à traiter est la sous-commande printf "subjectAltName=DNS:*.ycharbi.fr,DNS:*.docker.io,DNS:*.cloudflare.docker.com,IP:127.0.0.1"
présente à la ligne numéro 8. Utilisez les domaines que vous voulez voir validé par TLS lors de la poignée de main entre une application cliente et votre mandataire.
Tests de fonctionnement
Afin de tester notre préparation, nous pouvons lancer mitmproxy
de la façon suivante :
SSLKEYLOGFILE="/tmp/clés_tls_mitm.txt" mitmproxy --certs *=~/.mitmproxy/certs/cert.pem
où :
- SSLKEYLOGFILE= : emplacement du fichier contenant les clés négociées lors des différentes sessions TLS
- --certs *= : emplacement du certificat chaîné serveur pour signer le domaine spécifié dans la commande (ici
*
pour tout les domaines). Il peut y avoir plusieurs fois ce paramètre pour mettre autant de certificats que de domaines au besoin
Les clients HTTP communs peuvent êtres utilisés pour tester le fonctionnement de la solution
curl --proxy 127.0.0.1:8080 --cacert ~/.mitmproxy/certs/cert.pem https://doc.ycharbi.fr/index.php/Mitmproxy wget -e https_proxy=127.0.0.1:8080 --ca-certificate ~/.mitmproxy/certs/cert.pem https://doc.ycharbi.fr/index.php/Mitmproxy
L'exploitation du fichier de clés est expliqué dans le cas pratique de la section suivante.
Cas pratique
Comme énoncé en introduction, nous allons utiliser notre outil pour analyser les requêtes effectuées par un docker pull
. Cet outil étant particulièrement chiant avec HTTPS (comme la majorité des applications écrites en Golang), la mise en œuvre de notre capture représente un bon exercice.
Pour que cela fonctionne, il faut :
- un certificat avec les bons domaines renseignés dans le champ
subjectAltName
(fait dans l'étape préparatoire) - ajouter ce certificat dans la configuration du registre d'image Docker dont l'adresse sera remplacée par celle de notre mandataire (la commande de tirage de l'image reste identique)
- configurer Docker pour utiliser notre mandataire
- ajouter ce même certificat dans la base interne du système client
- rafraîchir la configuration de Systemd et relancer le démon Docker
Installation des paquets
Installation de Docker et de Tcpdump
apt install --no-install-recommends docker.io tcpdump
Le premier servira d'application TLS cliente et le second capturera le trafic chiffré. Celui-ci sera par la suite déchiffré par Wireshark à l'aide des clés enregistrées par Mitmproxy pendant la communication.
Configuration de l'environnement
Ajout du certificat du mandataire comme celui d'un registre d'images
mkdir -p /etc/docker/certs.d/127.0.0.1:8080 cp ~/.mitmproxy/certs/serveur.crt /etc/docker/certs.d/127.0.0.1:8080/ca.crt
Configuration du mandataire pour les requêtes au dépôt distant
mkdir -p /etc/systemd/system/docker.service.d
cat << '_EOF_' > /etc/systemd/system/docker.service.d/http-proxy.conf
[Service]
Environment="HTTPS_PROXY=https://127.0.0.1:8080"
_EOF_
Ajout du certificat dans la base interne du système
cp ~/.mitmproxy/certs/serveur.crt /usr/local/share/ca-certificates/ update-ca-certificates
Rechargement de la configuration de Systemd et du démon Docker
systemctl daemon-reload systemctl restart docker.service
ASTUCE
Il est possible d'afficher les variables d'environnements passées à l'exécutable de Docker par le service Systemd via la commandesystemctl show --property=Environment docker
afin de valider la bonne prise en compte de notre paramètre. Un docker search alpine
vous indiquera rapidement si votre installation est correcte. En cas de fonctionnement, le mandataire affichera la session et la commande renverra son résultat habituel. En cas d'erreur, une indication sur l'origine du problème rencontré par le client sera renvoyée dans la console.Tests de fonctionnement
Nous aurons besoin de trois consoles pour réaliser les opérations qui vont suivre. Un multiplexeur de terminal comme Tmux peut s'avérer bien utile dans de pareils circonstances. La commande tmux new -A -s toto
peut être utilisée pour initier un tel environnement (totalement facultatif).
Première console
Exécuter le mandataire
SSLKEYLOGFILE="/tmp/clés_tls_mitm.txt" mitmproxy --certs *=~/.mitmproxy/certs/cert.pem
Deuxième console
Exécuter l'enregistreur de trames
tcpdump -ni any -w /tmp/tls.pcap port not 22
Troisième console
Tirer une image du registre distant
docker pull alpine
Si tout est bon, votre image a été récupérée par docker
, tcpdump
a enregistré l'échange chiffré et mitmproxy
a journalisé les sessions en plus d'avoir enregistré les clés TLS de celles-ci. Il ne reste plus qu'à quitter les programmes des deux premières consoles pour en envoyer les fichiers de capture et de clés à une machine graphique possédant wireshark
pour déchiffrement et analyse.
scp /tmp/tls.pcap /tmp/clés_tls_mitm.txt ycharbi@\[2001:db8::1\]:/tmp/
Sur la machine graphique
Ouvrir le fichier de capture /tmp/tls.pcap
avec Wireshark et ajouter le fichier de clés /tmp/clés_tls_mitm.txt
dans les paramètres du protocole TLS : <ctrl>+<maj>+<p> > Protocols > TLS > (Pre)-Master-Sercret log filename > Parcourir... > /tmp/clés_tls_mitm.txt > OK
.
Les paquets TLS se déchiffrent instantanément, laissant apparaître les paquets HTTP qu'ils masquaient. Vous pouvez ainsi observer les URL que le client Docker requêtes pour récupérer ses images.