Capture de la poignée de main SSL avec tcpdump

By Flavien ROUX

SSL est le protocole le plus courant pour échanger des données cryptées sur une connexion TCP. Afin d’établir une connexion SSL, les deux points d’extrémité doivent échanger des clés publiques, l’algorithme de cryptage, la version du protocole, etc. Cet échange est connu sous le nom de poignée de main SSL.

Comme il s’agit d’un échange de clés asymétriques et de certificats, il y a souvent un risque que la poignée de main échoue. Par exemple, si le serveur a un certificat expiré ou si le client et le serveur ne peuvent pas négocier la version du protocole SSL/TLS, la poignée de main échouera.

Dans la plupart des cas, nous pouvons trouver la raison de l’échec en analysant les messages de la poignée de main SSL entre le client et le serveur. Dans ce tutoriel, nous allons étudier un moyen de capturer ces messages sur le réseau.

La commande tcpdump

La commande tcpdump nous permet de capturer les paquets TCP sur n’importe quelle interface réseau d’un système Linux.

En général, beaucoup de trafic TCP circule dans un échange SSL typique. Bien que tcpdump soit très utile et puisse capturer n’importe quelle quantité de données, il en résulte généralement des fichiers de vidage volumineux, parfois de l’ordre de plusieurs gigaoctets. Ces fichiers de vidage sont parfois impossibles à analyser. Par exemple, l’analyse de tels dumps dans Wireshark nécessiterait beaucoup de ressources.
freestar

Pour surmonter ce problème, la commande tcpdump fournit quelques options de filtrage. Ainsi, seuls les paquets TCP qui satisfont aux conditions de filtrage sont capturés dans le vidage de sortie.

Introduction à la poignée de main SSL

Passons rapidement en revue les messages que le client et le serveur échangent au cours de la poignée de main SSL :

Client Hello - Originaire du client. Il contient la version du protocole, les suites de chiffrement prises en charge par le client et un numéro aléatoire sécurisé.
Server Hello - Renvoyé par le serveur en réponse au Client Hello. Contient la version du protocole choisie par le serveur, la suite de chiffrement sélectionnée dans la liste du client, l'algorithme de chiffrement et d'autres extensions spécifiques à la version de TLS.
Certificat du serveur - Originé par le serveur. Contient la chaîne de certificats publics que le client authentifiera.
Demande de certificat - Originaire du serveur. Ce message n'est envoyé que si le serveur doit également authentifier le client, comme c'est le cas dans le SSL bidirectionnel.
Server Hello Done - Originaire du serveur. Indique la fin de Server Hello.
Certificat client - Renvoyé par le client en réponse à la demande du client. Le client envoie sa chaîne de certificats au serveur.
Client Key Exchange (échange de clés du client) - Provoqué par le client. Il génère un secret pré-maître et le crypte avec le certificat public du serveur. Il envoie ensuite le secret maître pour échanger l'algorithme de cryptage avec le serveur.
Certificate Verify - Originaire du serveur. Il indique la réussite de l'authentification du certificat du client.
Finished - Envoyé par le client et le serveur pour indiquer la réussite de l'authentification et de l'échange de clés.

En cas d’échec du SSL lors de l’établissement de la connexion, l’analyse des messages ci-dessus est un bon point de départ.

A lire également :   Pourquoi les distros Linux arrêtent les versions 32 bits (et ce que cela signifie pour vous)

Bien que nous ne discutions pas de ces messages en détail, il est important de réaliser que ces messages font partie des paquets de données TCP. Par conséquent, si nous voulons capturer uniquement ces messages, nous avons besoin d’options de filtrage avancées par rapport à celles que nous avons étudiées dans la dernière section.

Dans cette optique, explorons certaines des options de filtrage de données de tcpdump et voyons comment nous pouvons les utiliser pour filtrer uniquement les messages de poignée de main SSL.
freestar

Filtrage des messages SSL Handshake dans tcpdump

En plus des métadonnées comme le port ou l’hôte, la commande tcpdump prend également en charge le filtrage des données TCP. En d’autres termes, tcpdump nous permet de faire correspondre les octets de données dans le paquet avec une expression de filtre. Par exemple, nous pouvons filtrer les paquets avec certains drapeaux TCP :

tcpdump ‘tcp[tcpflags] & (tcp-syn|tcp-fin) != 0’.

Cette commande ne capturera que les paquets SYN et FIN et peut aider à analyser le cycle de vie d’une connexion TCP.

De la même manière, nous pouvons filtrer les messages de poignée de main SSL si nous connaissons la structure des octets de données. D’après la spécification TLS, nous savons que chaque message du protocole de poignée de main commence par une valeur numérique unique. Par exemple, tous les messages de poignée de main contiennent 22, représenté par 0x16 en hexadécimal, comme premier octet de données :
Dump TCP du Handshake SSL

Sur la base de ce fait, voyons comment nous pouvons filtrer les messages de poignée de main.

A lire également :   Pourquoi archiver le web ?

Capturer le Hello du client

Supposons que nous voulions analyser les tentatives d’établissement de connexion SSL d’un client. Pour cela, nous devons vérifier le message Client Hello entre le client et le serveur. Les messages Client Hello contiennent 01 dans le sixième octet de données du paquet TCP. Ainsi, pour filtrer de tels paquets :
freestar

tcpdump « tcp port 8081 and (tcp[((tcp[12] & 0xf0) >>2)] = 0x16) \
&& (tcp[((tcp[12] & 0xf0) >>2)+5] = 0x01) » -w client-hello.pcap

Comprenons les différentes parties des options de la commande :

tcp port 8081 - capture les paquets uniquement sur le port 8081, en supposant qu'il s'agit du port SSL du serveur d'application
et (tcp[12] & 0xf0) - lit le 13ème octet du paquet et conserve les 4 bits supérieurs
&& ((tcp[12] & 0xf0) >>2) - lorsque nous multiplions le résultat ci-dessus par 4, cela donne la taille de l'en-tête TCP.

Comme nous pouvons le voir dans le dump SSL ci-dessus, l’en-tête TLS précède le paquet de données TCP. Donc, pour obtenir le premier et le sixième octet de données, nous devons calculer la taille de l’en-tête TCP et sauter la correspondance de ces octets. C’est ce que font les deuxième et troisième termes ci-dessus.

Maintenant, tcp[Taille de l’en-tête TCP] pointe vers le premier octet de données du paquet. Et donc le terme, tcp[((tcp[12] & 0xf0) >>2)] = 0x16 vérifie si cet octet est égal à 22, le code numérique pour le handshake SSL. Et, tcp[((tcp[12] & 0xf0) >>2)+5] = 0x01 filtrera les paquets où le sixième octet est égal à 1, représentant le Client Hello.

De même, nous pouvons capturer n’importe quel message de poignée de main dont nous avons parlé précédemment. Par exemple, nous pouvons utiliser tcp[((tcp[12] & 0xf0) >>2)+5] = 0x02 pour les messages Server Hello.

Capture de paquets avec une version spécifique de TLS

Le protocole SSL a évolué au fil du temps. Après SSLv3, le protocole a été remplacé par TLS, qui est en grande partie similaire. Les applications modernes échangent généralement des messages sur TLSv1.3. Toutefois, beaucoup d’entre elles prennent encore en charge TLSv1.0, TLSv1.1 et TLSv1.2 pour des raisons de rétrocompatibilité.

A lire également :   Qu'est-ce que le format Audible ?

La spécification TLS attribue un code numérique unique à chaque version TLS :

SSLv3 - 0x300
TLSv1.0 - 0x0301
TLSv1.1 - 0x0302
TLSv1.2 - 0x0303
TLSv1.3 - 0x0304

Dans le message de poignée de main SSL, les dixième et onzième octets des données contiennent la version TLS. Par conséquent, un filtre tcpdump peut être appliqué :

tcpdump « tcp port 8081 and (tcp[((tcp[12] & 0xf0) >>2)] = 0x16) \
&& (tcp[((tcp[12] & 0xf0) >>2)+9] = 0x03) \N
&& (tcp[((tcp[12] & 0xf0) >>2)+10] = 0x03) »

Les termes tcp[((tcp[12] & 0xf0) >>2)+9] = 0x03 et tcp[((tcp[12] & 0xf0) >>2)+10] = 0x03 vérifient les dixième et onzième octets pour filtrer tous les paquets sur TLSv1.2. Cette commande capturera tous les paquets de poignée de main SSL où TLSv1.2 est échangé.
freestar

Capture des paquets de données d’application sur TLS

Jusqu’à présent, nous n’avons capturé que les messages de poignée de main SSL. Une fois la poignée de main terminée, le client et le serveur peuvent échanger les données d’application. Ces paquets de données d’application contiennent également la version TLS dans les deuxième et troisième octets de données :

tcpdump « tcp port 8081 and (tcp[((tcp[12] & 0xf0) >>2)] = 0x17) \
&& (tcp[((tcp[12] & 0xf0) >>2)+1] = 0x03) \
&& (tcp[((tcp[12] & 0xf0) >>2)+2] = 0x03)  » -w appdata.pcap

Ici, nous avons ajouté un filtre pour capturer les paquets qui ont 17 dans le premier octet et 03 dans les deuxième et troisième octets. En effet, 17 est le code numérique des paquets de données d’application, et 0303 indique TLSv1.2, comme nous l’avons vu précédemment.

Capturer les échecs de connexion SSL

Pour filtrer les échecs, nous allons vérifier le premier octet, qui contient 15 ou 21, en fonction de l’échec :

tcpdump « tcp port 8081 and (tcp[(((tcp[12] & 0xf0) >>2)] = 0x15) || (tcp[((tcp[12] & 0xf0) >>2)] = 0x21) » -w error.pcap

Cette commande capturera les paquets dont le premier octet de données est soit 15 soit 21.

Conclusion

Dans cet article, nous avons discuté des filtres tcpdump pour faire correspondre les données TCP dans un paquet avec une expression. En utilisant ces connaissances, nous pouvons facilement capturer les paquets dont les données correspondent à l’expression du filtre.

Nous avons ensuite utilisé cette approche pour capturer les paquets de poignée de main SSL en faisant correspondre un code numérique unique pour chaque message.