04/03/2007 - Déchiffrer une session SSL : méthodes et limitations
Tout le monde connait l'analyseur réseau Ethereal et son successeur Wireshark. Une des nouveautés de la version
0.99.5 est le déchiffrement de flux SSL dans certaines conditions.
Cette fonctionnalité est très intéressante car elle permet enfin de pouvoir analyser le trafic SSL à l'aide d'un sniffer que ce soit sur un serveur ( la plupart du temps HTTPS mais également LDAPS, POPS, SFTP, etc.. ) en production ou bien sur un port de recopie d'un flux (grâce à la fonction de mirroring d'un switch ou bien encore l'usage d'un tap physique).
Au niveau du client, il existe d'autres possiblités en utilisant des proxy (tels que l'excellent outil
WebScarab de l'
OWASP) on bien du tunneling SSL (avec
stunnel par exemple).
Pour être tout à fait exhaustif, l'outil
ssldump permettait déjà d'obtenir ce genre d'information, mais en shell. Son intégration à WireShark permet ainsi de suivre aisément et graphiquement les segments TCP, acknowledgements, retransmissions, etc..
Pour mettre en pratique cette fonctionnalité nous allons déchiffrer le flux HTTPs d'un serveur de test. Tout d'abord créeons le matériel crypto :
Nous générons un bi-clé RSA de 1024 bits :
# openssl genrsa -out server.key 1024
Generating RSA private key, 1024 bit long modulus
.......++++++ .++++++
e is 65537 (0x10001)
Notez la valeur de "e" : pour rappel, on a "ed= 1 mod (p-1)(q-1)" avec p et q grands nombre premiers choisis aléatoirement .
La valeur "pq" est la clef publique et "d" la clef privée. Bien que théoriquement libre, la valeur de "e" choisie par la plupart des implémentations est 3 , 5, 17 ou bien 65537.
# openssl rsa -in server.key -out server.pem
Nous créeons maintenant le CSR (Certficate Signing request) :
# openssl req -new -key server.key -out server.csr
You are about to be asked [..]
Country Name (2 letter code) [GB]:FR
State or Province Name (full name) [Berkshire]:Paris
Locality Name (eg, city) [Newbury]:75008
Organization Name (eg, company) [My Company Ltd]:RandCo
Organizational Unit Name (eg, section) []:IT
Common Name []:test.randco.fr
Email Address []:
Comme nous sommes dans un environnement de test, nous autosignons ce CSR pour obtenir le certificat :
# openssl x509 -req -in server.csr -signkey server.key -out server.crt
Signature ok
subject=/C=FR/ST=Paris/L=75008/O=RandCo/OU=IT/CN=test.randco.fr
Getting Private key
Nous obtenons ainsi une clé privée server.key et un certificat server.crt. Configurons ensuite Apache : nous ajoutons dans httpd.conf (au niveau du
virtual host écoutant sur le port 443) ceci :
DocumentRoot /usr/local/apache2/htdocs
ServerName test.randco.fr SSLEngine on
SSLCertificateFile /usr/local/apache2/etc/ssl.crt/server.crt
SSLCertificateKeyFile /usr/local/apache2/etc/ssl.key/server.pem
Cependant SSL n'est pas supporté par la version du binaire httpd actuel :
# httpd -l | egrep -i "(ssl|so)"
mod_so.c
#
Etant donné que nous utilisons Apache2, nous devons donc juste le recompiler en activant l'option --enable-ssl (contrairement à Apache 1.3 pour lequel le module mod_ssl est distribué à part) :
# ./configure --enable-ssl
# make
# make install
# httpd -l | egrep -i "(ssl|so)"
mod_ssl.c
mod_so.c
# apachectl -k restart
Une fois que le flux HTTPs aura été capturé par Wireshark, regardons comment configurer le déchiffrement dans Wireshark :
dans le menu Edit > Preferences > Protocols > SSL , remplir les champs comme suit :
- RSA keys list : les champs séparés par une virgule sont l'adresse IP du server, port TCP, protocole applicatif sous-jacent , chemin vers la clé privée
- SSL debug file : chemin vers l'indispensable fichier de debug
En appliquant un "SSL follow", on peut suivre la session et récuperer le contenu de la session en clair :
Pour tester à votre tour, voici la clé privée et le fichier de capture de la session HTTPS réalisée avec IE6. Les limitations de ssldump et de la méthode demeurent cependant :
- AES n'est supporté que dans la version CVS de ssldump (donc non intégrée à Wireshark)
- tous les flux chiffrés avec des Cipher Suite mettant en oeuvre un échange de clé du type Diffie-Helmann Ephemere (DHE) (en opposition avec RSA et Diffie Hellman statique - DH) restent évidemment indéchiffrables ( ceci en est d'ailleurs le but : cet algorithme reste toutefois sensibles aux attaques Man in the Middle. C'est la méthode choisie par Webscarab ou d'autres proxy tels que Spike Proxy ou Burp Proxy. Evidement, ce genre de manipulation provoque une alerte SSL au niveau du client : "certificat invalide" !).
La capture précédente était réalisée avec MS IE 6 (à jour de patches), le Cipher Suite négocié était TLS_RSA_WITH_RC4_128_MD5. Si on utilise d'autres outils (notamment Open Source tels que Firefox ou lwp-request, basé sur Perl) des Cipher Suite beaucoup plus forts seront négociés automatiquement :
# lwp-request -des https://localhost
200 OK
Connection: close
Server: Apache/2.0.53 (Unix) mod_ssl/2.0.53 OpenSSL/0.9.7a PHP/4.3.10 mod_perl/2 .0.1 Perl/v5.8.6
Content-Length: 2271
Content-Type: text/plain
Client-Peer: 127.0.0.1:443
Client-Response-Num: 1
Client-SSL-Cert-Issuer: /C=FR/ST=Paris/L=75008/O=RandCo/OU=IT/CN=test.randco.fr
Client-SSL-Cert-Subject: /C=FR/ST=Paris/L=75008/O=RandCo/OU=IT/CN=test.randco.fr
Client-SSL-Cipher: DHE-RSA-AES256-SHA
Client-SSL-Warning: Peer certificate not verified
Ce qui donne avec ssldump (version CVS) et le fichier de capture obtenu via :
# tcpdump -n tcp port 443 -i lo -w https2.cap -s 16436 &
(remarquez la taille des paquets à capturer inhabituellement grande : le MTU de l'interface de loopback est de 16436 octets !) :
# ./ssldump -r https2.cap -k server.key
New TCP connection
#1: 127.0.0.1(52260) <-> 127.0.0.1(443) 1 1 0.0004 (0.0004)
C>S SSLv2 compatible client hello Version 3.1 cipher suites
TLS_DHE_RSA_WITH_AES_256_CBC_SHA
TLS_DHE_DSS_WITH_AES_256_CBC_SHA
TLS_RSA_WITH_AES_256_CBC_SHA
TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA
TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA
TLS_RSA_WITH_3DES_EDE_CBC_SHA SSL2_CK_3DES
TLS_DHE_RSA_WITH_AES_128_CBC_SHA
TLS_DHE_DSS_WITH_AES_128_CBC_SHA
TLS_RSA_WITH_AES_128_CBC_SHA SSL2_CK_RC2
TLS_DHE_DSS_WITH_RC4_128_SHA TLS_RSA_WITH_RC4_128_SHA
TLS_RSA_WITH_RC4_128_MD5 SSL2_CK_RC4 SSL2_CK_RC464
TLS_DHE_DSS_EXPORT1024_WITH_DES_CBC_SHA
TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA
TLS_RSA_EXPORT1024_WITH_RC2_CBC_56_MD5
TLS_DHE_RSA_WITH_DES_CBC_SHA
TLS_DHE_DSS_WITH_DES_CBC_SHA
TLS_RSA_WITH_DES_CBC_SHA SSL2_CK_DES
TLS_DHE_DSS_WITH_RC2_56_CBC_SHA
TLS_RSA_EXPORT1024_WITH_RC4_56_SHA
TLS_RSA_EXPORT1024_WITH_RC4_56_MD5
TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA
TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA
TLS_RSA_EXPORT_WITH_DES40_CBC_SHA
TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5
SSL2_CK_RC2_EXPORT40
TLS_RSA_EXPORT_WITH_RC4_40_MD5
SSL2_CK_RC4_EXPORT40 1 2 0.0205 (0.0201)
S>C Handshake ServerHello Version 3.1 session_id[0]= cipherSuite TLS_DHE_RSA_WITH_AES_256_CBC_SHA
Ce qui veut donc dire TLS (SSL v3.1) avec échange de clés
Diffie Hellman Ephemere , algorithme de chiffrement par bloc
AES 256 bits et hashage par
SHA
compressionMethod NULL 1 3 0.0205 (0.0000)
S>C Handshake Certificate 1 4 0.0205 (0.0000)
S>C Handshake ServerKeyExchange 1 5 0.0205 (0.0000)
S>C Handshake ServerHelloDone 1 6 0.0531 (0.0325)
C>S Handshake ClientKeyExchange
DiffieHellmanClientPublicValue[128]= 7d 96 7e c0 16 ff 1b 63 26 4d 5a 1a b6 0e 16 62 e9 8d dd d6 a0 45 07 d2 51 50 61 d9 26 14 3b 3f bd 77 c5 66 ba 99 26 7e dc 52 c5 1c f2 72 2c dc 24 17 1e 58 a8 45 0d 46 3e 42 da 45 ad 1a c2 e2 87 c3 b8 3b 0f cc 12 c6 6b 98 db 25 7f da 37 8d f7 02 06 95 64 54 94 ec 57 eb 82 e3 76 ab 80 2d a6 da a2 d8 b3 51 a8 29 65 9c 35 fb 81 91 6f 91 e7 ab a9 75 f4 55 b1 30 09 97 13 29 fb a0 a6 a4 1 7 0.0531 (0.0000)
C>S ChangeCipherSpec 1 8 0.0531 (0.0000)
C>S Handshake 1 9 0.0697 (0.0165)
S>C ChangeCipherSpec 1 10 0.0697 (0.0000)
S>C Handshake 1 11 0.0700 (0.0003)
C>S application_data 1 12 0.0700 (0.0000)
C>S application_data 1 13 0.0703 (0.0003)
S>C application_data 1 14 0.0705 (0.0001)
S>C application_data 1 0.0716 (0.0011)
C>S TCP FIN 1 15 0.0717 (0.0001)
S>C Alert
Pour rappel, la valeur publique de l'échange Diffie Helman est "A = g^a mod p" avec un générateur "g" et un nombre premier "p" fixés suivant le groupe DH sélectionné. (dans le cas présent DHE -
Diffie Hellman en mode éphémère - cette valeur publique est signée à l'aide du certificat serveur afin de se prémunir des attaques
Man in the Middle)
Le nombre aléatoire "a" est seul connu de l'extremité qui publie A. Grâce à la théorie du logarithme discret, on sait qu' il est impossible (lire : long) connaissant "A","p" et "g" de déterminer "a".
Or "a" ( ou plus exactement "A^b mod p = B^a mod p") est utilisé pour construire la clé de session qui chiffrera le flux avec l'algorithme de chiffrement sélectionné (
stream cipher RC4 ou bien
block cipher AES dans les cas de ce présent article).
Ceci explique que
ssldump ne puisse obtenir la clé de session uniquement en observant le traffic et en connaissant la clef privée RSA si ce genre de
Cipher Suite est utilisé.
Le contenu de cet article est abordé en profondeur lors
notre formation "Réseaux Privés Virtuels" .