TLS vu d’avion
TLS (Transport Layer Security) est un protocole cryptographique permettant d’échanger des données de manière sécurisée au-dessus d’une session TCP établie entre un client et un serveur.
Les services offerts par TLS sont les suivants :
- Authentification du serveur et optionnellement du client,
- Chiffrement des données garantissant leur confidentialité,
- Integrité des données.
De nombreux protocoles applicatifs préexistants ont une variante transportée par TLS, faisant de TLS un des piliers d’Internet : HTTPS (HTTP sur TLS, sur le port 443), LDAPS (LDAP sur TLS, port 636) et ainsi de suite.
TLS est un standard maintenu par l’IETF et est le descendant du protocole SSL (Secure Sockets Layer). L’acronyme SSL continue d’être employé pour désigner TLS.
Les RFC décrivant TLS sont grosso modo découpées en trois sous-protocoles :
- Le TLS Record Protocol, décrivant le format des messages,
- Le Handshake, certainement la partie la plus complexe et centrale de TLS,
- Les alertes, qui sont des messages d’erreur.
Historique
SSL a été créé par la société Netscape et est devenu TLS en étant standardisé par l’IETF.
Netscape était l’éditeur du navigateur du même nom, qui fut le premier navigateur web diffusé à grande echelle, avant d’être supplanté par Internet Explorer.
Assez tôt, le besoin de garantir la sécurité des échanges de données se manifesta, notamment pour le commerce électronique. Ce besoin motiva la création de SSL au milieu des années 1990.
SSLv1
La toute première version de SSL a été élaborée en 1994. On trouve très peu d’informations sur SSLv1. En effet, le protocole comportait des vulnérabilités si graves qu’il n’est jamais sorti de Netscape.
SSLv2
Publiée en 1995, SSLv2 fut la toute première version de SSL réellement utilisée. Cette version comporte elle aussi de très sérieuses vulnérabilités de conception :
- Les sessions peuvent être facilement terminées par un attaquant en position de MitM (Man-in-the-Middle) : il lui suffit d’injecter un
TCP FIN. - La même clé de session est utilisée pour le chiffrement et l’integrité des données : si un algorithme de chiffrement faible est négocié, l’integrité peut également être compromise.
- L’integrité est assurée par un calcul du style
h = MD5(k + M), oùkest la clé de session,Mle message à authentifier et+désigne la concaténation. Ce type de construction est à proscrire, car un attaquant connaissanthpeut forgerh1 = MD5(k + M + M1)afin de remplacer(M, h)par(M + M1, h1). - L’integrité du handshake n’est pas assurée par un message
Finishedcontenant un résumé cryptographique des messages du handshake (il y a bien des messagesClientFinishedetServerFinishedmais ils ne contiennent pas de résumé cryptographique sur le handshake). Cela permet à un attaquant en position de MitM de manipuler le handshake et par exemple de modifier à la baisse les algorithmes de chiffrement négociés.
Pour toutes ces raisons, SSLv2 ne doit plus être utilisé. Ça tombe bien, il est à peu près abandonné aujourd’hui (en partie grâce à la RFC 6176) et l’on ne s’attardera donc pas dessus.
SSLv3
SSLv3 est une refonte majeure destinée à corriger les nombreuses erreurs de jeunesse de SSLv2. Cette version, relativement proche des suivantes (jusqu’à TLSv1.3) a été publiée en 1996.
Elle a fait l’objet de la RFC 6101 en 2011, qui est une RFC à vocation « historique ».
TLSv1.0
TLSv1.0 est le résultat de la normalisation de SSLv3 par l’IETF dans la RFC 2246, publiée en 1999. Même s’il n’y a pas de changement fondamental entre SSLv3 et TLSv1.0, il y a quelques différences « subtiles » empêchant l’interopérabilité.
Les principales différences entre SSLv3.0 et TLSv1.0 introduites lors de cette normalisation sont les suivantes :
- L’usage combiné de MD5 et SHA-1 dans plusieurs opérations cryptographiques (calcul du
master_secretà partir dupre_master_secret, calcul dukey_blockà partir dumaster_secretnotamment) est remplacé dans TLSv1.0 par une PRF. - Le calcul du message
Finished, initialement réalisé à partir de MD5 et SHA-1 et maintenant réalisé avec cette PRF. - Le MAC est calculé différemment.
- La signature présente dans le message
CertificateVerify(envoyé lorsque le client s’authentifie) est calculée différemment.
TLSv1.1
Cette version a été normalisée par la RFC 4346 en 2006. Les principales modifications par rapport à TLSv1.0 sont les suivantes :
- L’algorithme AES est introduit.
- Le vecteur d’initialisation (IV) implicite est remplacé par un IV explicite, afin d’empêcher certaines attaques (BEAST, notamment).
- Les code d’alerte envoyés lorsqu’un message est mal déchiffré et lorsqu’un message a un MAC incorrect sont uniformisés, afin d’empêcher d’éventuels oracles de padding.
- Les ciphers suites EXPORT (DES40, RC4_40, RC2) et des mécanismes de key exchange EXPORT (DHE_DSS_EXPORT, DHE_RSA_EXPORT, DH_anon_EXPORT, DH_DSS_EXPORT, DH_RSA_EXPORT, RSA_EXPORT) sont supprimées.
TLSv1.2
La RFC 5246 décrivant TLSv1.2 a été publiée en 2008. Les principales différences avec la version précédentes sont :
- La pseudorandom function (PRF) utilisée pour diverses opérations cryptographiques a été revue. Initialement basée sur MD5 et SHA-1, elle utilise maintenant SHA-256 ; Globalement, tous les calculs cryptographiques basés sur l’utilisation conjointe de MD5 et SHA-1 sont remplacés par l’utilisation d’une seule fonction de hachage.
- Des modes de chiffrement authentifiants ont été introduits.
- Les suites utilisant DES et IDEA ont été retirées.
- Le support de AES devient obligatoire (la suite TLS_RSA_WITH_AES_128_CBC_SHA doit être supportée).
- Des suites utilisant HMAC-SHA256 ont été ajoutées.
TLSv1.3
TLSv1.3 est une refonte en profondeur du protocole dont de multiples aspects ont été entièrement repensés :
- Auparavant, les suites de cryptographiques définissaient l’algorithme de chiffrement, la fonction de hachage utilisée par le mécanisme d’authentification, le mécanisme d’échange du secret commun et le mécanisme d’authentification. Dans TLSv1.3, les opérations « symetriques » et les opérations « asymétriques » ont été décorélées et les suites cryptographiques ne définissent plus que l’algorithme de chiffrement et le mécanisme assurant l’integrité. Seules cinq suites cryptographiques sont définies. Toutes garantissent la Perfect Forward Secrecy (ou PFS : la compromission de la clé privée du serveur ne permet pas de déchiffrer le trafic capturé auparavant).
- La négociation du mécanisme d’authentification utilise maintenant l’extension
signature_algorithms. - Les messages
ClientKeyExchangeetServerKeyExchangeutilisés pour l’échange des demi-clés ont été remplacés par l’extensionkey_share. En fait, TLSv1.3 fait un usage massif des extensions. - Une différence majeure par rapport aux versions antérieures est que cet échange de demi-clés est fait AVANT l’étape d’authentification : le client envoie une liste de demi-clés pour les groupes qu’il supporte. Le serveur répond la demi-clé pour le groupe qu’il a choisi et tous les messages suivants sont chiffrés, y compris les messages de handshakes postérieurs.
- Autre différence majeure, la suite du handshake est chiffrée, notamment l’envoi du certificat.
- Le mécanisme de dérivation de clés a été entièrement revu : un jeu de clés différent est utilisé pour protéger les messages du handshake et les message applicatifs.
- Le message
ChangeCipherSpec, sorte de « clé de contact » activant le matériel cryptographique et algorithmes négociés n’est plus utilisé. - Le message
CertificateVerify, auparavant uniquement utilisé par le client lorsqu’il doit s’authentifier, est maintenant utilisé par chaque partie pour s’authentifier.
Le handshake TLSv1.2 et inférieur
handshake sans PFS
Le handshake suit la cinématique ci-dessous :
|--------ClientHello------>|
| |
|<-------ServerHello-------|
|<-------Certificate-------|
|<-----ServerHelloDone-----|
| |
|----ClientKeyExchange---->|
|-----ChangeCipherSpec---->|
|---------Finished-------->|
| |
|<----ChangeCipherSpec-----|
|<--------Finished---------|
| |
|<==Données applicatives==>|
Dans cet échange,
- Le client demande l’ouverture d’une session TLS en envoyant un
ClientHelloau serveur. Ce message annonce les suites cryptographiques qu’il supporte. - Le serveur répond alors :
- Un
ServerHellomentionnant la suite cryptographique choisie par le serveur, - Un
Certificate, contenant le chaîne de certificats prouvant l’identité du serveur, - Un
ServerHelloDone.
- Un
- Si l’authentification est un succès, le client répond un
ClientKeyExchangequi contient lepre_master_secretqu’il a généré, chiffré avec la clé publique du serveur.
Chaque coté a alors tout ce qu’il faut pour dériver le materiel crytographique. Le client envoie donc :
- Un
ChangeCipherSpecannonçant l’activation des mécanismes et matériels cryprographiques négociés, - Un
Finishedqui est un résumé cryptographique sur les messages échangés au cours du handshake.
Si la vérification du message Finished par le serveur est satisfaisante, ce dernier envoie à son tour un ChangeCipherSpec et un message Finished.
Si la vérification de ce dernier message par le client est concluante, l’échange de données applicatives peut commencer.
Handshake avec PFS
Lorsque la suite cryptographique assure la PFS, le handshake suit la cinématique ci-dessous :
|--------ClientHello------>|
| |
|<-------ServerHello-------|
|<-------Certificate-------|
|<----ServerKeyExchange----|
|<-----ServerHelloDone-----|
| |
|----ClientKeyExchange---->|
|-----ChangeCipherSpec---->|
|---------Finished-------->|
| |
|<----ChangeCipherSpec-----|
|<--------Finished---------|
| |
|<==Données applicatives==>|
Cet échange se produit lorsque la suite cryptographique retenue par le serveur garantit la PFS.
Dans ce cas, le pre_master_secret ne sera pas généré par le client, mais grâce à un échange Diffie-Hellman.
C’est pour cela qu’un message supplémentaire, ServerKeyExchange, est ici présent : il contient la demi-clé du serveur, signée par sa clé publique.
De plus, le ClientKeyExchange ne contient pas le pre_master_secret chiffré par la clé publique du serveur, mais la demi-clé du client.
Handshake avec authentification mutuelle
La plupart du temps, seul le serveur s’authentifie, mais TLS offre la possibilité au serveur d’authentifier le client.
Le handshake (on considère un handshake avec PFS mais l’utilisation d’un échange Diffie-Hellman n’a rien d’obligatoire lorsque le client s’authentifie) ressemble alors à ceci :
|--------ClientHello------>|
| |
|<-------ServerHello-------|
|<-------Certificate-------|
|<----ServerKeyExchange----|
|<---CertificateRequest----|
|<-----ServerHelloDone-----|
| |
|----Certificate---------->|
|----ClientKeyExchange---->|
|----CertificateVerify---->|
|-----ChangeCipherSpec---->|
|---------Finished-------->|
| |
|<----ChangeCipherSpec-----|
|<--------Finished---------|
| |
|<==Données applicatives==>|
L’authentification du client se fait ainsi :
- Le serveur envoie un message
CertificateRequestafin de demander au client de s’authentifier. Ce message peut contenir une liste des autorités de certification acceptées. - Le client envoie un message
Certificatecontenant sa chaîne de certificats. - Le client envoie un message
CertificateVerifyafin de prouver qu’il possède la clé privée associée à son certificat. Ce message contient donc une signature portant sur les messages de handshake échangés auparavant.
Le handshake TLSv1.3
Le handshake TLSv1.3 est sensiblement différent du handshake des version antérieures : chaque partie envoie sa demi-clé avant l’authentification du serveur. Cela a pour intérêt de garantir la confidentialité de la suite du handshake et d’économiser un aller-retour (un « RTT », Round-Time-Trip).
Ce schéma, fortement inspiré d’un de ceux présents dans la RFC, décrit la cinématique d’un handshake « usuel ». Pour chaque message, un + est présent pour mentionner les extensions particulièrement importantes, comme l’extension key_share du ClientHello, qui transporte la demi-clé du client.
Un message entouré d’accolades (comme EncryptedExtensions) est un message de handshake chiffré avec le matériel cryptographique du handshake.
Un message entouré de crochets (comme Application Data) est un message applicatif chiffré avec le matériel cryptographique dédié aux messages applicatifs.
Dès que le le serveur a généré sa demi-clé, il est en mesure de dériver du materiel cryptographique pour protéger la suite du handshake.
Contrairement aux version antérieures, le serveur ne prouve pas son identité en signant sa demi-clé : l’authentification est assurée par l’envoi du CertificateVerify, prouvant la possession de la clé privée associée au certificat envoyé dans le message Certificate.
Chaque partie envoie un Finished récapitulant cryptographiquement le handhsake, puis dérive le matériel cryptographique applicatif.
|-ClientHello---------------------------------->|
|+ key_share |
|+ signature_algorithms |
| |
|<----------------------------------ServerHello-|
| + key_share |
| {EncryptedExtensions} |
| {Certificate} |
| {CertificateVerify} |
| {Finished} |
| |
|-{Finished}----------------------------------->|
| |
| [Application Data]<------->[Application Data] |
Les alertes TLS
Les alertes sont des messages de 1 octet envoyés lorsqu’un problème survient. Une alerte peut être fatale (la session TLS doit être immédiatement terminée) ou non.
Parmi les alertes les plus courantes on trouve :
certificate_revoked - 44;certificate_expired - 45;certificate_unknown - 46;unknown_ca - 48;protocol_version - 70.
Les formats de message
Les messages TLS possèdent la structure suivante :
+------+---------+--------+---------+
| type | version | length | payload |
+------+---------+--------+---------+
type: sur 2 octets, ce champ indique si le message est un message dechange_cipher_spec(0x14), d’alert(0x15), dehandshake(0x16), ou un message applicatif (0x17).version: sur 2 octets, indique le numéro de version de TLS.length: sur 2 octets, indique la longueur (en octets) de la payload.
Les messages de handshake
Avant TLSv1.3
ClientHello
Ce message contient à minima :
client_version, la version de TLS que le client souhaite utiliser ;- La liste de suites cryptographiques supportées par le client, par ordre décroissant de préférence ;
- La liste des algorithmes de compression supportés par le client ;
- Le
client.random, sur 32 octets ; - Un identifiant de session, noté
session_id, d’une longueur variant entre 0 et 32 octets ; - D’éventuelles extensions.
Certaines extensions sont très courantes, notamment le SNI (Server Name Indication), qui indique le nom de domaine que le client souhaite contacter.
ServerHello
Ce message contient à minima :
server_version, la version de TLS choisie par le serveur ;- Le
server.randomsur 32 octets ; - Le
session_id; - La suite cryptographique choisie par le serveur ;
- L’algorithme de compression choisi par le serveur ;
- D’éventuelles extensions.
Certificate
Ce message contient la chaîne de certificats destinées à prouver l’identité de son émetteur.
ServerKeyExchange
Ce message n’est envoyé que si le mécanisme d’échange de clé de la suite cryptographique négocié est basé sur un échange Diffie-Hellman.
Il contient alors la demi-clé du serveur, signée à l’aide de sa clé privée (en fait cette signature porte aussi sur le client_random et le server_random).
CertificateRequest
Ce message n’est envoyé que si le serveur exige que le client s’authentifie. Il contient une liste (éventuellement vide) des autorité de certification que le serveur souhaite que le client utilise.
ServerHelloDone
Ce message n’a pas de contenu. Sa seule fonction est de signaler la fin des messages (ServerHello + Certificate + ServerKeyExchange + CertificateRequest) envoyés par le serveur.
ClientKeyExchange
Ce message contient le pre_master_secret chiffré avec la clé publique du serveur lorsque le mécanisme d’échange de clé de la suite cryptographique négociée est RSA. Lorsque ce mécanisme est un échange Diffie-Hellman, ce message contient la demi-clé du client.
CertificateVerify
Ce message, envoyé uniquement lorsque le client doit s’authentifier, est une signature calculée avec la clé privée du client sur la concaténation des messages de handshake reçus auparavant.
ChangeCipherSpec
Ce message n’a pas de contenu et n’est pas un message de handshake à proprement parler.
Sa seule fonction est de signaler que son émetteur va maintenant utiliser le materiel et les mécanismes cryptographiques négociés auparavant pour protéger ses messages ultérieurs.
Finished
Ce message est un résumé cryptographique de l’ensemble des messages échangés au cours du handshake. C’est le premier message chiffré avec les clés et algorithmes venant d’être négociés.
L’envoi de ce message empêche un attaquant de modifier à son profit les messages de handhake.
Son calcul a évolué au fil des versions :
- Dans SSLv3, c’est la concaténation de
MD5(master_secret + pad2 + MD5(handshake_messages + Sender + master_secret + pad1))etSHA(master_secret + pad2 + SHA(handshake_messages + Sender + master_secret + pad1));, oùSenderest entier de 32 bits identifiant l’émetteur (client ou serveur) ; - Dans TLSv1.0 et TLSv1.1, c’est les 12 premiers octets de
PRF(master_secret, finished_label, MD5(handshake_messages) + SHA-1(handshake_messages)); - Dans TLSv1.2, c’est le résultat de
PRF(master_secret, finished_label, Hash(handshake_messages)), oùPRFest la fonction décrite plus bas, utilisant la fonction de hachage de la suite cryptographique négociée.
Que se passerait-il sans message Finished ?
Le handshake aurait alors la forme suivante :
|--------ClientHello------>|
| |
|<-------ServerHello-------|
|<-------Certificate-------|
|<----ServerKeyExchange----|
|<-----ServerHelloDone-----|
| |
|----ClientKeyExchange---->|
|-----ChangeCipherSpec---->|
| |
|<----ChangeCipherSpec-----|
Le scénario suivant serait alors possible :
|--------ClientHello------>|--------ClientHello---------->|
| | |
|<-------ServerHello-------|<-------ServerHello-----------|
|<-------Certificate-------|<-------Certificate-----------|
|<-----ServerHelloDone-----|<-----ServerHelloDone---------|
| | |
|----ClientKeyExchange---->|----ClientKeyExchange_bis---->|
|-----ChangeCipherSpec---->|-----ChangeCipherSpec-------->|
| | |
|<----ChangeCipherSpec-----|<----ChangeCipherSpec---------|
|<==Données applicatives==>|<====Données applicatives====>|
L’attaquant remplace le ClientKeyExchange envoyé par le client par un ClientKeyExchange_bis contenant un pre_master_secret_bis choisi par l’attaquant en position de MitM.
Ensuite,
- Le
ServerHelloDonereste inchangé, - L’attaquant ne peut pas connaître le
pre_master_secretdu client, ne peut donc pas connaître lekey_blockdu client, n’est donc pas capable de déchiffrer les messages applicatifs émis par le client et va se contenter de les détruire purement et simplement. - L’attaquant connaît par contre le
key_blockutilisé par le serveur et est donc en mesure de lui envoyer le message de son choix.
Dans TLSv1.3
Dans TLSv1.3, certains messages disparaissent (ClientKeyExchange, ServerKeyExchange, ChangeCipherSpec), le CertificateVerify change de rôle et l’usage des extensions est systématisé.
ClientHello
Le ClientHello de TLSv1.3 est légèrement différent, car, en plus des champs présents dans les versions antérieures, il embarque toujours l’extension supported_versions, qui liste les versions de TLS supportées par le client.
Il embarque également les extensions suivantes (dans le cas d’un handshake « usuel » comme schématisé plus haut) :
- Extension
key_share: cette extension est une liste deKeyShareEntry, chacun de ces éléments étant un couple formé de l’identifiant sur deux octets d’un groupe Diffie-Hellman et d’une demi-clé calculé dans dans le groupe en question. - Extension
signature_algorithms: cette extension contient une liste d’identifiants sur deux octets d’un algorithme de signature qui sera utilisé pour le calcul de la signature calculée dans leCertificateVerifyet sur la demi-clé du serveur.
ServerHello
Comme le ClientHello, le ServerHello de TLSv1.3 embarque obligatoirement l’extension supported_versions.
Dans le cas d’un handshake « usuel » (pas d’utilisation de PSK, etc), une extension key_share contenant une unique KeyShareEntry est présente.
Certificate
Ce message contient la chaîne de certification de l’émetteur, comme dans les versions précédentes.
CertificateVerify
Ce message est une signature calculée avec la clé privée de l’émetteur et portant sur un haché (le Transcript-Hash) de la concaténation des messages de handshake antérieures et du message Certificate de l’émetteur.
Dans TLSv1.3, c’est ce message qui authentifie son émetteur.
Finished
Le message Finished de TLSv1.3 a le même rôle de résumé cryptographique du handshake que dans les versions antérieures, mais son calcul diffère : c’est un HMAC calculé sur le Transcript-Hash (encore lui) de la concaténation des message de handshake, du Certificate et du CertificateVerify de l’émetteur.
Les suites cryptographiques
Avant TLSv1.3
Jusqu’à TLSv1.2, une suite de chiffrement est un quadruplet spécifiant :
- Un mécanisme de Key Exchange,
- L’algorithme à clé publique utilisé par le serveur,
- Un algorithme de chiffrement symetrique,
- La fonction de hachage utilisée par le HMAC.
Exemples :
TLS_NULL_WITH_NULL_NULL: la suite qui ne fait rien.SSL_RSA_EXPORT_WITH_DES40_CBC_SHA: suite export utilisant une clé RSA de 512 bits, DES en mode CBC avec une clé de 40 bits et SHA-1 comme fonction de hachage.SSL_RSA_WITH_DES_CBC_SHA: la même suite en version non-export.SSL_DH_RSA_WITH_3DES_EDE_CBC_SHA: authentification avec RSA, échange de clé avec Diffie-Hellman statique, chiffrement avec 3DES en mode CBC, SHA-1 comme fonction de hachage.SSL_DH_anon_WITH_3DES_EDE_CBC_SHA: pas d’authentification, échange de clé avec Diffie-Hellman anonyme, chiffrement avec 3DES en mode CBC, SHA-1 comme fonction de hachage.SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA: authentification avec RSA, échange de clé avec Diffie-Hellman éphémère, chiffrement avec 3DES en mode CBC, SHA-1 comme fonction de hachage.TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA: la même suite, renommée dans TLSv1.0.TLS_RSA_WITH_AES_256_CBC_SHA256: authentification et échange de clé avec RSA, chiffrement avec AES en mode CBC, SHA256 comme fonction de hachage.TLS_DHE_RSA_WITH_AES_256_CBC_SHA256: authentification avec RSA, échange de clé avec Diffie-Hellman éphémère, chiffrement avec AES en mode CBC, SHA256 comme fonction de hachage.
Dans TLSv1.3
Seul cinq suites cryptographiques existent dans TLSv1.3. Contrairement aux versions antérieures du protocole, ces suites spécifient uniquement l’algorithme de chiffrement et le mécanisme d’integrité.
Ces suites sont :
TLS_AES_128_GCM_SHA256 (0x1301);TLS_AES_256_GCM_SHA384 (0x1302);TLS_CHACHA20_POLY1305_SHA256 (0x1303);TLS_AES_128_CCM_SHA256 (0x1304);TLS_AES_128_CCM_8_SHA256 (0x1305).
Le mode GCM (Galois Counter Mode) est un mode authentifiant : les données sont chiffrées avec le mode CTR (chaque bloc est xoré avec un compteur chiffré, le compteur étant incrémenté à chaque bloc) et un MAC est calculé en réalisant des xor et des multiplications dans le corps fini GF(2^128) sur les blocs chiffrés.
Le mode CCM (Counter mode with Cbc-Mac) est un autre mode authentifiant. Il utilise également le mode CTR pour le chiffrement, mais le calcul du MAC est réalisé avec un algorithme apparenté à CBC-MAC.
La fonction de hachage mentionnée est la fonction de hachage utilisée par la fonction de dérivation de clé (HKDF) et lors du calcul du Transcript-Hash.
Dérivation du matériel cryptographique de session
Avant TLSv1.3
Les versions de TLS antérieures à TLSv1.3 dérivent le matériel cryptographique à partir du master_secret en utilisant une PseudoRandomFunction construite à partir de fonctions de HMAC.
On commence par considérer la fonction de HMAC associée à la fonction de hachage hash :
HMAC_hash(secret, message);
À partir de cette fonction, on construit une fonction P_hash « étirant » une seed à l’aide d’un secret :
P_hash(secret, seed) = HMAC_hash(secret, A(1) + seed) +
HMAC_hash(secret, A(2) + seed) +
HMAC_hash(secret, A(3) + seed) + ...
La suite A() étant définie par récurrence :
A(0) = seedA(i) = HMAC_hash(secret, A(i-1))
Avant TLSv1.2, la fonction PRF combine P_MD5 et P_SHA-1 via un xor :`
PRF(secret, label, seed) = P_MD5(S1, label + seed) XOR
P_SHA-1(S2, label + seed);
Dans TLSv1.2, seule la fonction de hachage SHA-256 est utilisée :
PRF(secret, label, seed) = P_<hash>(secret, label + seed)
Dans tous les cas, la fonction PRF ainsi définie est utilisée comme ci-dessous pour dériver le matériel cryptographique à partir :
- Du
master_secret, calculé à partir du secret commun, - Du
server_randomque serveur envoie dans sonServerHello, - Du
client_randomque le client envoie dans sonClientHello.
key_block = PRF(SecurityParameters.master_secret,
"key expansion",
SecurityParameters.server_random +
SecurityParameters.client_random);
Le master_secret est lui-même dérivé à partir du secret commun, le pre_master_secret, en utilisant PRF également :
master_secret = PRF(pre_master_secret, "master secret",
ClientHello.random + ServerHello.random)[0..47];
Le pre_master_secret est :
- Généré par le client et envoyé chiffré avec la clé publique du serveur dans le message ClientKeyExchange,
- ou bien généré à partir via un échange Diffie-Hellman. Dans ce cas,
- le client envoie son
g^a mod pdans unClientKeyExchange, - le serveur envoie son
g^b mod psigné avec sa clé privée dans unServerKeyExchange
- le client envoie son
Dans ce dernier cas, le pre_master_secret est le résultat de cet échange Diffie-Hellman.
L’utilisation du key_block varie selon les versions et les algorithmes de chiffrement :
- Dans SSLv3.0 et TLSv1.0, il est découpé en
client_write_MAC_secret,server_write_MAC_secret(clés utilisées pour les calculs de HMAC),client_write_key,server_write_key(clés utilisées pour le chiffrement),client_write_IVetserver_write_IV(vecteurs d’initialisation pour le chiffrement du premier message lorsque le mode CBC est utilisé). - Dans TLSv1.1 et TLSv1.2, pour contrer l’attaque BEAST les vecteurs d’initialisation sont explicites et se situent au début des messages applicatifs. Un vecteur d’initialisation est cependant extrait du
key_blockpour initialiser lenoncesi un mécanisme AEAD (comme AES-GCM) est utilisé.
Dans TLSv1.3
La dérivation de clé a été entièrement repensée dans TLSv1.3. Elle utilise deux fonctions, HKDF-Extract et HKDF-Expand, définies dans la RFC 5869.
Le schéma de dérivation de clés de TLSv1.3 introduit une séparation entre clés protégeant le handshake et clés protégeant le trafic applicatif : une compromission des clés de handshake n’aura pas d’impact sur les clés applicatives.
La fonction HKDF-Extract se réduit à un appel à une fonction de HMAC : HKDF-Extract(salt, IKM) = HMAC-Hash(salt, IKM),
La fonction HKDF-Expand utilise une clé secrète PRK, une chaîne de caractère info qui fait office de label et un entier L spécifiant le nombre d’octets à produire.
Le calcul de OKM = HKDF-Expand(PRK, info, L) est basé sur la construction par récurrence d’une chaîne T :
T(0)est la chaîne vide,`T(1) = HMAC-Hash(PRK, T(0) | info | 0x01),T(2) = HMAC-Hash(PRK, T(1) | info | 0x02),T(3) = HMAC-Hash(PRK, T(2) | info | 0x03),
et ainsi de suite et T = T(1) | T(2) | T(3) | ... | T(N), où N = 1 + (L/HashLen).
Finalement, OKM est égal aux L premiers octets de T.
La RFC 8446 décrivant TLSv1.3 définit une fonction HKDF-Expand-Label à partir de HKDF-Expand :
HKDF-Expand-Label(Secret, Label, Context, Length) =
HKDF-Expand(Secret, HkdfLabel, Length)
Où HkdfLabel est la concaténation de la chaîne de caractères "tls13 " + Label + Context et de la longueur de cette chaîne.
Ainsi qu’une fonction Derive-Secret, construite à partir de HKDF-Expand-Label :
Derive-Secret(Secret, Label, Messages) =
HKDF-Expand-Label(Secret, Label,
Transcript-Hash(Messages), Hash.length)
La dérivation des clés dans TLSv1.3 est entièrement récapitulée par la figure présente au paragraphe 7.1 de la RFC 8446 :
0
|
v
PSK -> HKDF-Extract = Early Secret
|
+-----> Derive-Secret(., "ext binder" | "res binder", "")
| = binder_key
|
+-----> Derive-Secret(., "c e traffic", ClientHello)
| = client_early_traffic_secret
|
+-----> Derive-Secret(., "e exp master", ClientHello)
| = early_exporter_master_secret
v
Derive-Secret(., "derived", "")
|
v
(EC)DHE -> HKDF-Extract = Handshake Secret
|
+-----> Derive-Secret(., "c hs traffic",
| ClientHello...ServerHello)
| = client_handshake_traffic_secret
|
+-----> Derive-Secret(., "s hs traffic",
| ClientHello...ServerHello)
| = server_handshake_traffic_secret
v
Derive-Secret(., "derived", "")
|
v
0 -> HKDF-Extract = Master Secret
|
+-----> Derive-Secret(., "c ap traffic",
| ClientHello...server Finished)
| = client_application_traffic_secret_0
|
+-----> Derive-Secret(., "s ap traffic",
| ClientHello...server Finished)
| = server_application_traffic_secret_0
|
+-----> Derive-Secret(., "exp master",
| ClientHello...server Finished)
| = exporter_master_secret
|
+-----> Derive-Secret(., "res master",
ClientHello...client Finished)
= resumption_master_secret
Dans le cas d’un handshake « usuel », le secret commun issu de l’échange Diffie-Hellman est dérivé en un Handshake Secret avec un appel à HKDF-Extract.
Cette valeur est dérivée, à l’aide de Derive-Secret, en une valeur différente pour le client (client_handshake_traffic_secret) et pour le serveur (server_handshake_traffic_secret), valeur qui sera par la suite utilisée pour la dérivation des clés qui seront employées pour protéger les messages de handhake, selon la relation
[sender]_write_key = HKDF-Expand-Label(Secret, "key", "", key_length)
[sender]_write_iv = HKDF-Expand-Label(Secret, "iv", "", iv_length)
[sender]_write_key et [sender]_write_iv sont utilisées comme secret et nonce par le chiffrement AEAD.
Une seconde dérivation est réalisée pour protéger les messages applicatifs : le Handshake Secret est dérivé en un Master Secret moyennant un appel à Derive-Secret puis HKDF-Extract.
Ce Master Secret est ensuite dérivé en un client_application_traffic_secret_0 ou server_application_traffic_secret_0 suivant le côté de la connexion, secret à son tour employé pour dériver une clé de chiffrement et un vecteur d’initialisation, selon la relation ci-dessus.
Le chiffrement des messages applicatifs
Cas des chiffrements par flot
Si le mécanisme de chiffrement est un algorithme de chiffrement par flot (RC4, typiquement), alors un message chiffré est obtenu en chiffrant ceci :
+---------------+------+
| message clair | HMAC |
+---------------+------+
Le HMAC est défini par la formule :
HMAC_hash(MAC_write_secret, seq_num + TLSCompressed.type +
TLSCompressed.version + TLSCompressed.length +
TLSCompressed.fragment));
MAC_write_secretest la clé de calcul de HMAC extraite dekey_block;seq_numest un numéro de séquence sur 64 bits destiné à empêcher le rejeu et incrémenté à chaque message. Il vaut initialement zéro.TLSCompressed.typeest le type du message clair,TLSCompressed.versionest la version de TLS utilisée,TLSCompressed.lengthest la taille du message clair après éventuelle compression,TLSCompressed.fragmentest le message en clair, éventuellement compressé.
Cas des chiffrements par bloc en mode CBC
Si le mécanisme de chiffrement est un algorithme de chiffrement par bloc et que le mode opératoire est CBC, un message chiffré est obtenu en chiffrant ceci :
+---------------+------+---------+
| message clair | HMAC | padding |
+---------------+------+---------+
Le message chiffré envoyé C et le message clair M sont donc reliés par la relation C = E(M, HMAC, Padding).
La longueur du padding est telle que le bloc (message clair + hmac + padding) a une taille multiple de la taille de bloc de l’algorithme de chiffrement.
À partir de TLSv1.0, chaque octet de padding a une valeur égale à cette longueur (ainsi le padding peut être 0x01, ou bien 0x0202, ou bien 0x030303, etc). Dans SSLv3, seul le dernier octet de padding doit avoir une valeur égale à la longueur du padding.
Le HMAC est calculé de la même manière que dans le cas d’un chiffrement par flot.
En réponse à certaines attaques (BEAST notamment), la gestion du vecteur d’initialisation (IV) a été modifiée dans le mode CBC à partir de TLSv1.1.
Avant TLSv1.1, le premier message était chiffré avec un IV calculé lors de la dérivation du master_secret en key_block et les messages suivants utilisaient comme IV le dernier bloc du message chiffré précédent.
À partir de TLSv1.1, l’IV est explicitement mentionné dans le message. Le message chiffré est calculé comme auparavant, mais commence maintenant par l’IV et l’on a donc la relation :
C = IV + E(M, HMAC, Padding).
Mac-Then-Encrypt ou Encrypt-Then-Mac ?
À l’origine, TLS utilise Mac-Then-Encrypt, il concatène le MAC du message clair à la fin de ce dernier, ajoute du padding et chiffre le tout.
Plusieurs travaux théoriques (https://link.springer.com/content/pdf/10.1007/3-540-44448-3_41.pdf et https://eprint.iacr.org/2002/078.pdf) ont montré que le mode Encrypt-Then-Mac (on chiffre le message clair, on calcule le MAC du message chiffré résultant et on concatène ce MAC à la fin du message chiffré) est préférable.
De plus, le mode Mac-Then-Encrypt de SSL/TLS offre un contexte favorable pour un oracle de padding, la vérification du padding étant réalisée avant la vérification du MAC.
Avant TLSv1.1, le code d’alerte n’était pas le même en cas de padding incorrect (decryption_failed) et en cas de MAC (bad_record_mac), fournissant ainsi un oracle de padding.
La RFC 7366, publiée en 2014, décrit une extension permettant de négocier l’utilisation du mode Encrypt-Then-Mac : si le client souhaite utiliser ETM, il ajoute l’extension encrypt-then-mac à son ClientHello.
Si le serveur est d’accord, il ajoute l’extension encrypt-then-mac à son ServerHello.
Lorsque cette extension est utilisée,
- Le message chiffré
Cest calculé viaC = E(M + padding)en cas d’usage de TLSv1.0 où l’IVest implicite, ouC = IV + E(M + padding)pour TLSv1.1 et TLSv1.2 où l’IVest explicite ; - Le calcul du MAC est relativement similaire à celui réalisé dans le cas historique :
MAC = HMAC_hash(MAC_write_secret + seq_num + type + version + length + C).
Cas des chiffrements authentifiants (mode AEAD) dans TLSv1.2
TLSv1.2 introduit l’utilisant de mode authentifiant comme GCM ou CCM (on parle de mode AEAD, Authenticated Encryption with Associated Data).
Un message chiffré C est calculé à partir du message clair M via la relation C = nonce_explicit + AEAD(M). Concrètement, AEAD est ici AES en mode GCM ou CCM.
AEAD prend en paramètre :
- Le message clair
M, - Le
nonce, qui est la concaténation d’un vecteur d’initialisationIVextrait dekey_blocket de la partie « explicite »nonce_explicit:nonce = IV + nonce_explicit, - les données additionnelles qui sont la concaténation du numéro de séquence
seq_numévoqué plus haut, du type du message, de la version de TLS et de la taille du message clair.
Le résultat AEAD(M) contient un tag authentifiant dans son dernier bloc.
Chiffrement en mode AEAD dans TLSv1.3
TLSv1.3 utilise exclusivement des modes authentifiants et effectue le chiffrement différemment de TLSv1.2 :
- Le
nonceest maintenant complètement implicite et est défini comme étant le xor du vecteur d’initialisation provenant de la dérivation de clés et duseq_num. - Le bloc
additional_dataest quasi-identique à celui de TLSv1.2, la seule différence étant qu’il ne contient plusseq_num, si bien queC = AEAD(M).
Les mécanismes de reprises de session
Le session_id
Lors d’un handshake « normal », le serveur transmet via son ServerHello un identifiant de session, le session_id.
Cet identifiant peut être utilisé pour reprendre une session antérieure : le client inclut ce session_id dans son ClientHello.
Si ce session_id est connu du serveur, alors le matériel cryptographique négocié lors du handshake initial est directement réutilisé, sans nouvelle authentification du serveur ni négociation du pre_master_secret :
Client Server
ClientHello -------->
ServerHello
[ChangeCipherSpec]
<-------- Finished
[ChangeCipherSpec]
Finished -------->
Application Data <-------> Application Data
Le session_id ne contient aucun secret en soi (comme il est échangé au tout début du handshake il est de toute façon public !) et est juste un index auquel client et serveur associent une sauvegarde les clés de la session en question.
Les tickets de session
Le mécanisme à base de session_id a deux inconvénients :
- La RFC déconseille de sauvegarder les sessions plus de 24 heures. Il n’est donc pas approprié pour le maintien d’une session à long terme.
- Le serveur doit conserver le matériel cryptographique associé à la session, ce qui est pénalisant si le serveur a des ressources limitées, ou interagit avec un grand nombre de clients.
Le mécanisme de ticket de session décrit dans la RFC 4507, puis 5077, décrit comment déporter dans le client le stockage des éléments de session.
En premier lieu, le client doit demander un ticket de session au serveur, chose qu’il fait en incorporant une extension SessionTicket dans son ClientHello. Si le serveur supporte ce mécanisme, il le signale en incorporant également un SessionTicket dans son ServerHello, puis envoie un message NewSessionTicket avant son ChangeCipherSpec.
Cette cinématique est récapitulée par ce schéma issu de la RFC 5077 :
Client Server
ClientHello
(empty SessionTicket extension)-------->
ServerHello
(empty SessionTicket extension)
Certificate*
ServerKeyExchange*
CertificateRequest*
<-------- ServerHelloDone
Certificate*
ClientKeyExchange
CertificateVerify*
[ChangeCipherSpec]
Finished -------->
NewSessionTicket
[ChangeCipherSpec]
<-------- Finished
Application Data <-------> Application Data
S’il souhaite reprendre la session initiale, le client dans son ClientHello une extension SessionTicket contenant le SessionTicket envoyé par le serveur.
Le matériel cryptographique précédent est réutilisé sans authentification du serveur ni échange de clé et le serveur renouvelle son ticket de session :
Client Server
ClientHello
(SessionTicket extension) -------->
ServerHello
(empty SessionTicket extension)
NewSessionTicket
[ChangeCipherSpec]
<-------- Finished
[ChangeCipherSpec]
Finished -------->
Application Data <-------> Application Data
Le ticket de session est une sauvegarde de l’état de la session (suite de chiffrement, master_secret, etc) chiffrée avec une clé symétrique uniquement connue du serveur : sans cela, un attaquant interceptant ce ticket pourrait totalement compromettre la session.
Les informations présentes dans le ticket de session sont donc uniquement destinées au serveur : le client doit stocker le master_secret et autres éléments de l’état de la session « à coté » du ticket de session.
La reprise de session dans TLSv1.3
Dans TLSv1.3, les mécanismes à base de session_id et de ticket de session sont obsolètes et la reprise de session utilise les mécanismes de PSK propres à cette version.
Vulnérabilités dans TLS
La suite du document est une liste – forcément non exhaustive – de vulnérabilités intéressantes ayant affectées SSL/TLS.
Ces vulnérabilités peuvent être
- dues aux algorithmes cryptographiques employés par SSL/TLS,
- inhérentes au protocole SSL/TLS,
- causées par des erreurs d’implémentation.
Les vulnérabilités cryptographiques
Ces vulnérabilités peuvent être du fait des algorithmes employés par TLS, ou être causés par la manière dont TLS les utilise.
Oracle de padding dans RSA
L’attaque par oracle de padding a été publiée en 1998 par Daniel Bleichenbacher dans un article nommé « Chosen Ciphertext Attacks Against Protocols Based on the RSA Encryption Standard PKCS#1 ».
Les trois principaux ingrédients de cette attaque sont :
- le fait que la fonction RSA est homomorphe :
RSA(xy) mod n = xy^e mod n = (x^e mod n) * (y^e mod n) = RSA(x) * RSA(y) mod n. - la manière dont est paddée un bloc de donnée chiffré avec RSA.
- la présence d’un oracle de padding dans le mécanisme de déchiffrement : ce mécanisme se comporte différemment selon que le padding du message déchiffré soit valide ou non.
Dans SSLv3, le pre_master_secret est chiffré selon la version 1.5 du standard PKCS#1.
Ce standard décrit comment une payload doit être paddée avant d’être chiffrée avec une clé publique RSA :
+----+----+---------+----+---------+
| 00 | 02 | padding | 00 | payload |
+----+----+---------+----+---------+
<------------ 8*k bits ------------>
Les deux octets de poids fort doivent donc être 0x00 et 0x02.
Du fait de cette structure, le message
m = 0x02 + padding + 0x00 + payload
vérifie les inégalités
2^(8*k - 15) <= m < 2^(8*k - 15) + 2^(8k - 16).
Si l’attaquant ne connaît pas m (il souhaite le retrouver !),
il connaît c = m^e mod n.
Il peut également calculer c1 = (c * s^e) mod n = (m*s)^e mod n
pour la valeur s de son choix.
Le fait que le message (m*s) soit valide entraîne les inégalités suivantes :
2^(8*k - 15) <= m*s < 2^(8*k - 15) + 2^(8k - 16).
En choisissant convenablement s, l’attaquant peut, au fil des requêtes, obtenir un encadrement de plus en plus précis de m et in fine déterminer sa valeur.
Ainsi, cette attaque peut être menée de la manière suivante lorsque le pre_master_secret est entièrement généré par le client :
- L’attaquant est en position de MitM et intercepte le trafic échangé entre le client et le serveur ;
- Il lance l’attaque contre le serveur. ici
cest le contenu duClientKeyExchange. Chaque tentative nécessite d’initier un handshake au cours duquel est envoyé unClientKeyExchangemodifié contenantc1.
L’attaque permet de retrouver le pre_master_secret après de l’ordre d’un million de requêtes.
Cette attaque a été corrigée par plusieurs contremesures :
- Suppression de l’oracle de padding dans TLSv1.0 ;
- Utilisation à partir de TLSv1.1 de la version 2 de PKCS#1 qui rend cette attaque impossible.
L’utilisation d’un échange Diffie-Hellman rend également inopérante cette attaque.
Oracle de padding dans CBC
Comme la précédente, cette attaque exploite le fait qu’un oracle de padding va divulguer de l’information sur un message chiffré. Cependant, ce n’est pas le pre_master_secret qui est visé ici, mais le contenu applicatif.
L’attaque par oracle de padding dans le mode CBC a été découverte par Serge Vaudenay au début des années 2000 (https://www.iacr.org/archive/eurocrypt2002/23320530/cbc02_e02d.pdf) et appliquée à SSL peu de temps après (https://www.iacr.org/cryptodb/archive/2003/CRYPTO/1069/1069.pdf).
Dans ce dernier article, la mise en oeuvre de l’attaque permettait de retrouver le mot de passe d’un compte IMAP en une durée de l’ordre de l’heure.
Pour rappel, dans le mode CBC le chiffrement d’un bloc de clair fait intervenir le bloc de chiffré précédent, selon la relation :
Yi = E(Xi ^ Yi-1).
Comme ce mode est utilisé par des algorithmes de chiffrement par bloc, la taille des données à chiffrer doit être un multiple de la taille du bloc, ce qui impose d’utiliser un padding.
Une façon courante de padder est d’ajouter à padding où chaque octet de padding a une valeur égale à la longueur de ce padding (ainsi le bloc de padding est 0x01, ou 0x0202, ou bien 0x030303, etc).
Un oracle de padding est présent dans la fonction réalisant le déchiffrement si cette dernière se comporte différemment selon que ce padding soit valide ou non.
Supposons donc que deux blocs, (C1, C2), chiffrés de (P1, P2), aient été interceptés.
Le but final de l’attaquant est de retrouver P2, qui vérifie la relation P2 = D(C2) ^ C1.
Pour cela, il va soumettre à la fonction de déchiffrement ayant un oracle de padding des messages (Ç1, C2), où Ç1 = C1 ^ delta.
L’attaquant commence par choisir delta au hasard.
Il soumet à l’oracle des couples (Ç1, C2), en modifiant le dernier octet de delta.
Au bout d’un certain nombre d’itérations (256 au plus), un des (Ç1, C2) finit par être accepté, au sens où D(C2) ^ Ç1 est un bloc correctement paddé.
Lorsque cet événement se produit, la situation la plus probable est que le dernier octet de D(C2) ^ Ç1 soit 0x01, ce qui détermine la valeur de D(C2).
L’attaquant peut alors modifier le dernier octet de delta de façon à s’assurer que le dernier octet de D(C2) ^ Ç1 vaille 0x02. Il va alors soumettre des couples (Ç1, C2) en jouant sur l’avant-dernier octet de delta, jusqu’à ce que (Ç1, C2) soit accepté : le padding de D(C2) ^ Ç1 est de nouveau correct et l’hypothèse la plus vraisemblable est que ce bloc se termine par 0x0202, ce qui détermine l’avant-dernier octet de D(C2).
À ce stade, les deux derniers octets de D(C2) sont connus et en itérant le procédé l’attaquant finit par déterminer D(C2), et donc P2 = D(C2) ^ C1.
Un oracle de padding était présent dans les versions antérieures à TLSv1.1, qui reagissaient différemment selon que le padding d’un message soit incorrect (l’alerte decryption_failed était retournée) ou que le MAC soit incorrect (l’alerte bad_record_mac était retournée). Comme la vérification du MAC est réalisé après la vérification du padding, un MAC incorrect indique en creux que le padding est correct.
Cet oracle a été corrigé dans TLSv1.1, qui retourne bad_record_mac dans tous les cas.
Cela ne règle pas complètement la question, comme l’attaque Lucky 13 l’a montré : si le padding est correct, le padding est vérifié et le traitement du message sera un peu plus long, ce qui constitue une fuite d’information.
Les faiblesses statistiques de RC4
Contrairement à la plupart des algorithmes de chiffrement symétriques utilisés par SSL/TLS, RC4 est un algorithme de chiffrement par flot : il génère une suite d’octets avec lesquels sont xorés les octets de message clairs.
Par conséquent, pour que l’algorithme soit cryptographiquement « solide », il faut que la suite d’octets générés ait de « bonnes » propriétés statistiques et soit indistinguable d’une suite générée au hasard.
RC4 a été créé par Ronald Rivest (le ‘R’ de ‘RSA’ !) en 1987. Dès 1995, des faiblesses ont été detectées dans RC4.
Malgré cela, l’algorithme, très simple d’implémentation et très rapide, a été massivement utilisé (dans la première moitié des années 2010, jusqu’à 50 % du trafic SSL/TLS était chiffré avec RC4 !).
Une percée majeure a été publiée en 2013 (https://web.archive.org/web/20130922170155/http://www.isg.rhul.ac.uk/tls/RC4biases.pdf) par des chercheurs anglais qui ont montré que l’exploitation de biais statistiques permettait de retrouver un octet de clair parmis les 256 premiers octets envoyés lors d’une session TLS, pour peu que le même message soit envoyé dans 2^32 sessions différentes.
C’est typiquement le cas d’un identifiant de connexion (cookie ou assimilé) qui sera envoyé tout au long de sa durée de vie au fil des sessions TLS (si l’on reste connecté plusieurs jours, voire plusieurs mois sur marmiton.org, le même identifiant de connexion sera envoyé dès le début de chaque session TLS).
Le nombre de sessions nécessaire (plus de quatre milliards !) reste cependant très élevé.
Un nouveau résultat spectaculaire fut publié en 2015 (https://www.rc4nomore.com/) par des chercheurs hollandais.
Leur attaque permet, dans les mêmes circonstances, de retrouver un cookie si il est envoyé 2^27 fois. Cette attaque est une ciphertext-only attack : il suffit de collecter suffisamment de trafic chiffré pour pouvoir la réaliser, ce qui la rend réaliste.
Finalement, l’usage de RC4 dans TLS a été interdit par la RFC 7465, publiée en 2015.
Collision de blocs chiffrés : Sweet 32
Une faiblesse générique des algorithmes de chiffrement par bloc est qu’au bout d’un certain nombre de messages chiffrés, des collisions se produisent, collisions qui peuvent faire fuiter de l’information sur les messages clairs.
Par exemple, dans le mode CBC chaque bloc de chiffré est calculé avec la formule Yi = Ek(Xi ^ Yi-1). Si deux blocs, Yi et Yj sont en collision,
alors Yi = Yj, d’où
Ek(Xi ^ Yi-1) = Ek(Xj ^ Yj-1), donc en déchiffrant
Xi ^ Yi-1 = Xj ^ Yj-1, ce qui entraîne
Xi ^ Xj = Yi-1 ^ Yj-1 : le xor de deux messages clair est égal au xor (connu !) de deux messages chiffrés, ce qui peut avoir des conséquences facheuses si la nature des échanges fait qu’une partie de Xi peut être connue.
Du fait du paradoxe des anniversaires, une collision entre deux messages chiffrés a une probabilité significative de se produire dès que le nombre de messages chiffrés est égal à la racine carrée du nombre de bloc possibles.
Plus clairement, dans le cas d’un algorithme de chiffrement utilisant des blocs 64 bits (comme DES, 3DES ou Blowfish), alors une collision a une chance significative de se produire dès que que 2^32 bloc ont été échangés.
Cette vulnérabilité générique était connue depuis longtemps, mais en 2016 des chercheurs s’y sont intéressé d’un peu plus près, en tirant profit de certaines particularités du trafic visé, comme :
- l’envoi répété d’un secret fixe (ce qui est typiquement le cas d’un cookie),
- Le fait qu’une partie du plaintext est connue (comme
GET HTTP/1.1\r\n, par exemple).
L’équipe en question a également utilisé une technique du type « Man-In-The-Browser », qui s’apparente à une attaque CSRF : les attaquants, qui s’intéresse à la connexion de la victime sur lapin.com, l’amènent à visiter attaquant.org, qui va télécharger un script du style :
<script>
je fais plein de requêtes vers lapin.com
</script>
L’attaque est détaillée sur https://sweet32.info/SWEET32_CCS16.pdf.
Ici la Same-Origin-Policy interdit au script de prendre connaissance de la réponse à ses requêtes, mais qu’importe, le seul but du script est de générer une grande quantité de trafic vers lapin.com embarquant le cookie visé.
L’attaque permet d’extraire un cookie de 16 octets après avoir intercepté 705 Go de trafic, généré en 18,6 heures.
Indépendamment des mécanismes propres au navigateur (« l’amplification de trafic » basée sur le script malveillant ne marche plus dans le cas d’un token JWT ou d’un cookie dont l’attribut SameSite est correctement positionné), une contremesure simple est de n’utiliser que des algorithmes utilisant des blocs de 128 bits.
Les suites EXPORT
Entre le moment où elle est sortie des entités régaliennes et son adoption massive pour des usages quotidiens, la cryptographie a connu dans les années 1990 une phase de transition où son utilisation était régulée par des lois imposant des restrictions en terme de résistance à la cryptanalyse.
Aux États-Unis, jusqu’en 1999 l’export de logiciel ou matériel embarquant des mécanismes cryptographiques ayant des clés symétriques de plus de 40 bits ou des clés asymétriques de plus de 512 bits était par conséquent interdit. Par conséquent, des suites cryptographiques « exportables » furent incorporées dans SSLv3.
Dans ces suites export, l’algorithme de chiffrement (RC2, RC4 ou DES) utilisait une clé de chiffrement de 40 bits, la clé privée de RSA ou DSS avait une taille de 512 bits.
Le bridage à 40 bits se faisait en utilisant un mécanisme de dérivation dédié. L’usage de ces suites exportables a été interdit à partir de TLSv1.1.
DHE versus DH
Lorsqu’une suite utilisant le mécanisme d’échange DHE est utilisée, le serveur envoie au client dans un message ServerKeyExchange une demi-clé Ys =g^s mod p signée et le client lui envoie un ClientKeyExchange contenant une demi-clé Yc = g^c mod p. Chaque partie reconstitue g^sc mod p qui constitue le pre_master_secret.
Si le trafic est intercepté et que la clé privée du serveur est compromise, un attaquant purement passif ne peut pas faire grand chose : il devrait savoir résoudre le problème du logarithme discret pour retrouver le pre_master_secret, les secrets s et c étant éphémères.
Comme le nom ne l’indique pas, les suites utilisant le mécanisme DH sont assez différentes : ici, le serveur utilise un secret s qui a une longue durée de vie : en cas de compromission du serveur, il est tout aussi susceptible d’être volé que la clé privée. Ces suites n’assure donc pas la PFS.
DH anonymous
Dans ces suites, la demi-clé du serveur n’est pas signée : un attaquant peut parfaitement la remplacer par une demi-clé de son choix. Autant dire que ces suites n’offrent aucune sécurité !
Les vulnérabilités protocolaires
Ces vulnérabilités sont dues à des incohérences ou a des erreurs de conception inhérentes au protocole TLS.
BEAST
L’attaque BEAST (Browser Exploit Against Ssl/Tls), publiée en 2011, exploite le fait que jusque à TLSv1.0, le dernier bloc chiffré d’un message est utilisé comme vecteur d’initialisation pour chiffrer le prochain message.
Considérons une connexion TLS, dans laquelle l’attaquant aimerait déterminer la valeur d’un bloc P_i.
Le bloc chiffré C_i est calculé selon la relation C_i = E(P_i ^ C_i-1).
Le bloc C_i fait partie d’un message TLS, dont le dernier bloc C_j-1 est utilisé comme vecteur d’initialisation du prochain message TLS envoyé.
Ainsi, on a la relation C_j = E(P_j ^ C_j-1).
Notre attaquant est en mesure d’observer le trafic TLS : Il connaît donc les blocs C_i, C_i-1, C_j-1, C_j. De plus, il contrôle P_j.
Dans cette situation, il est capable de déterminer si le bloc P_i vaut ou non x :
Pour cela, il utilise P_j = C_j-1 ^ C_i-1 ^ x. Si l’on a bien P_i = x, alors
C_j = E(C_j-1 ^ Pj) = E(C_j-1 ^ C_j-1 ^ C_i-1 ^ x) = E(C_i-1 ^ x) = E(C_i-1 ^ Pi) = C_i.
Dans le scénario décrit par l’article https://nerdoholic.org/uploads/dergln/beast_part2/ssl_jun21.pdf, l’attaquant tente de retrouver le cookie de session authentifiant la victime sur https://www.pigeon.fr.
Ce scénario peut être mis en oeuvre ainsi :
- L’attaquant amène la victime visiter
attaquant.org, qui télécharge un script envoyant des requêtes vershttps://www.pigeon.fr. Le cookie de la victime sera incorporé à ces requêtes. - L’attaquant est en mesure d’observer le trafic chiffré généré par ce script.
Le script va alors forger des requêtes de la forme POST /PAAAAAAAAAAAAAAAAAAAAAAAAAAADDING HTTP /1.1\r\n vers https://www.pigeon.fr. Comme le cookie de la victime est incorporé à la requête, la requête effectivement émise est en fait POST /PAAAAAAAAAAAAAAAAAAAAAAAAAAADDING HTTP /1.1\r\nCookie: adm=patate_douce.
Cette requête est découpée en plusieurs bloc lors avant chiffrement :
P_i-3 = POST /PAAAAAAAAAP_i-2 = AAAAAAAAAAAAAAAAP_i-1 = AADDING HTTP /1.P_i = 1\r\nCookie: adm=p.
Ainsi, un seul octet de P_i est inconnu. Pour toutes les 256 valeurs possibles de cet octet, l’attaquant
- construit
xcomme étant égal au 15 premiers octets deP_i, qui sont fixes et connus, suivi de l’octet-candidat, - soumet une requête commençant par
P_j = C_j-1 ^ C_i-1 ^ x. - Il est en mesure de forger une telle requête, car il peut observer
C_j-1etC_i-1.
Si x est égal à P_i, alors C_j = C_i.
En jouant sur la longueur du padding, l’attaquant peut aboutir à
P_i = \r\nCookie: adm=pa,
deviner le 2ème caractère du cookie de session, puis provoquer l’envoi de
P_i = \nCookie: adm=pat,
deviner le 3ème octet du cookie de session et ainsi de suite.
Compression, le revers de la médaille – CRIME
CRIME est l’acronyme de Compression Ratio Info-leak Made Easy. Cette attaque découverte en 2012 utilise le taux de compression des messages comme canal auxiliaire, lorsque TLS utilise un mécanisme de compression.
Vu d’avion, un algorithme de compression supprime les redondances présentes dans un message et réduira plus fortement la taille d’un message comportant beaucoup de redondances qu’un message en comportant peu.
Cette propriété peut être exploitée pour retrouver un cookie, en mettant en oeuvre une attaque « Man-In-The-Browser » :
On vise le cookie auth=sandwichbanane que la victime incorpore aux requêtes qu’elle envoie à https://www.poulet.fr.
L’attaquant amène la victime à visiter attaquant.org, qui va télécharger un script.
Le script enverra des requêtes HTTP à https://www.poulet.fr, avec le cookie visé et un cookie supplémentaire auth=UneValeurInitialementAléatoire.
L’attaquant, également en position de MiTM, est capable de mesurer la taille des paquets TLS envoyés à https://www.poulet.fr. Lorsque la taille du paquet envoyé diminue, cela signifie qu’un des caractères du cookie a été trouvé.
En effet, le script va envoyer des requêtes de ce type :
GET /truc HTTP/1.1
Host:www.poulet.fr
Bla
Cookie: auth=sandwichbanane;auth=ilkdfjsdlkfjls
or cette requête sera moins fortement compressée que celle-ci :
GET /truc HTTP/1.1
Host:www.poulet.fr
Bla
Cookie: auth=sandwichbanane;auth=sakdfjsdlkfjls
qui sera elle-même moins compressée que celle-là :
GET /truc HTTP/1.1
Host:www.poulet.fr
Bla
Cookie: auth=sandwichbanane;auth=sandwisdlkfjls
et ainsi de suite.
Compression, le revers de la médaille – BREACH
BREACH pour Browser Reconnaissance and Exfiltration via Adaptative Compression of Hypertext.
Comme CRIME, cette attaque exploite le taux de compression des messages comme canal auxiliaire ; Cependant, ici, c’est l’utilisation d’un mécanisme de compression par la couche HTTP qui est visée.
Compression, le revers de la médaille – TIME
Comme BREACH, l’attaque TIME (Time Info-leak Made Easy) se base sur l’utilisation de compression.
Compression, le revers de la médaille – HEIST
HEIST (HTTP Encrypted Information Stolen through TCP-windows) est un raffinement des attaques exploitant le taux de compression comme canal auxiliaire : les modifications de la taille de la fenêtre TCP entre client et serveur est utilisée pour inférer la longueur des messages échangés.
La durée de traitement d’un message comme oracle de padding – Lucky 13
Ou le retour de l’oracle de padding dans CBC. Lorsque le mode Mac-Then-Encrypt est utilisé, un MAC sur le message clair est calculé et ajouté à la fin de ce message. Le tout est paddé, puis chiffré.
Jusqu’à SSLv3, le message d’alerte envoyé lorsque le padding était incorrect n’était pas le même que lorsque le MAC était erroné, constituant ainsi un oracle de padding.
Même avec cette contremesure, un oracle de padding reste présent : si le padding est correct, le MAC sera calculé et le récepteur renverra le message d’alerte un peu moins vite que si le padding est incorrect.
SLOTH
SLOTH (acronyme de « Security Losses from Obsolete and Truncated Transcript Hashes »), publiée en 2016 (https://www.mitls.org/pages/attacks/SLOTH), englobe une série d’attaques dans lesquelles l’utilisation de fonctions de hachage obsolètes (MD5 et SHA-1) dans l’algorithme de signature d’un certificat entraîne une dégradation significative de la sécurité de la session TLS.
L’attaque la plus spectaculaire est probablement celle permettant à un attaquant d’usurper l’identité d’un client dans le cas d’une authentification mutuelle. Elle exploite le fait qu’il est calculatoirement possible de trouver des collisions à prefixes choisis dans MD5, c’est-à-dire qu’étant donnés P1 et P2 choisis, il est possible de calculer C1 et C2 tels que MD5(P1 + C1) = MD5(P2 + C2). Mener une telle attaque demande de réaliser 2^39 calculs MD5.
Le scénario de l’attaque est le suivant :
- L’attaquant est en position de MitM entre le client et le serveur. Il possède un certificat signé par une autorité de certification connue du client et souhaite se faire passer pour le client auprès du serveur.
- Le client se connecte à l’attaquant. Ils échangent successivement un
ClientHello, unServerHello, unServerCertificateet unServerKeyExchange. - L’attaquant lance alors une recherche de collision MD5 à préfixes choisis : il utilise comme préfixe
P1la concaténation des messages échangés jusqu’ici avec le client et pour préfixeP2leclient.randomqu’il va incoroporer auClientHelloqu’il enverra au serveur. Les élémentsC1etC2retournés par l’algorithme de recherche de collision est utilisé d’une part comme extensions incorporées dans leClientHelloqu’il enverra au serveur, comme liste d’autorité de certifications à inclure dans le message CertificateRequest qu’il enverra au client d’autre part. - L’attaquant initie alors un handshake avec le serveur et poursuit le handshake débuté avec le client en parallèle.
- Le client envoie à l’attaquant un
ClientVerify, calculée avec sa clé privée et portant une signature calculée sur le haché MD5 des messages antérieurs du handshake. Comme ce haché est identique au haché MD5 des messages échangés entre l’attaquant et le serveur, la signature présente dans ceClientVerifyest identique à celle que calculerait l’attaquant s’il possédait lui-même la clé privée du client. LeClientVerifyenvoyé par le client peut donc être transmis au serveur par l’attaquant. - À ce stade, l’attaquant cesse les échanges avec le client et termine le handshake avec le serveur.
- Une fois le handshake avec le serveur terminé, l’attaquant est authentifié avec l’identité du client auprès du serveur.
Oracle causé par le support de SSLv2 : DROWN
DROWN est l’acronyme de « Decrypting Rsa using Obsolete and Weakened eNcryption ».
Cette attaque tire parti du fait qu’un nombre significatif de serveurs TLS, au moment de sa publication en 2016, continuait de supporter SSLv2.
L’attaque permet de retrouver le pre_master_secret envoyé par le client lors d’un handshake, en utilisant la pile SSLv2 du serveur comme oracle de padding.
Dans le protocole SSLv2, le client envoie un message ClientMasterKey, contenant un master_secret chiffré avec la clé publique du serveur : c’est l’équivalent du ClientKeyExchange des versions postérieures.
Le master_secret fait 16 octets. Cependant, lorsqu’une suite cryptographique EXPORT est utilisée, 11 octets sont envoyés en clair et seul 5 octets sont chiffrés dans le ClientMasterKey.
Lorsqu’il reçoit le ClientMasterKey, le serveur en extrait le master_secret, dérive les clés de session et envoie un message ServerVerify.
Un handshake SSLv2 est initié par le ClientHello du client, auquel le serveur répond par un ServerHello. Ce ServerHello contient un challenge.
Ce challenge est ré-envoyé chiffré avec la clé de session du serveur dans le ServerVerify.
Par ailleurs, lorsqu’il reçoit un master_secret qui n’est pas paddé correctement, le serveur génère un master_secret aléatoire.
Ces différents éléments permettent d’utiliser le handshake SSLv2 comme un oracle de padding :
- L’attaquant initie un handshake SSLv2 avec le serveur, en demandant l’utilisation d’une suite EXPORT.
- Le serveur envoie un challenge dans le
ServerHello. - l’attaquant envoie un
ClientMasterKey(dont il ne connaît pas le contenu) au serveur. - si le
master_secretest paddé correctement, le serveur l’utilise pour chiffrer son challenge. S’il n’est pas paddé correctement, il chiffre le challenge avec unmaster_secretaléatoire. Dans tous les cas, le challenge chiffré est envoyé dans leServerVerify. - l’attaquant déchiffre le contenu du
ServerVerifyavec les 2^40 valeurs possibles pour la partie secrète dumaster_secret(rappelons que si une suite EXPORT est utilisée, 88 bits dumaster_secretsont publics). Si lemaster_secreta été paddé correctement, alors pour une de ces 2^40 valeurs, la valeur déchiffrée est égale au challenge envoyé. Ce n’est pas le cas en cas de padding incorrect.
Finalement, l’attaque suit la logique suivante :
- L’attaquant collecte des handshakes TLS où RSA est utilisé pour l’échange de clé,
- L’attaquant met en oeuvre une heuristique qui tente de convertir les
pre_master_secretde 48 octets, chiffrés dans leClientKeyExchange, enClientMasterKeycontenant 5 octets chiffrés. - Chaque
ClientMasterKeyainsi construit est utilisé dans une invocation de l’oracle de padding, c’est-à-dire dans un handshake SSLv2 utilisant une suite EXPORT.
Finalement, l’attaque permet de récupérer un pre_master_secret chiffré avec une clé RSA de 2048 bits avec le coût suivant :
- capture de 1000 handshakes TLS,
- 40000 handshake SSLv2,
- Coût calculatoire de l’ordre de 2^50 opérations.
L’équipe de chercheurs (dont les résultats sont décrits en détail dans https://drownattack.com/drown-attack-paper.pdf) a pu mener l’attaque en moins de 8 heures pour 440 $ sur Amazon EC2.
Ces chiffres sont génériques : dans certaines versions d’OpenSSL, des erreurs d’implémentation de SSLv2 facilitaient l’attaque.
Oracle causé par le support de SSLv3 : Poodle
Publiée en 2014, POODLE (Padding Oracle on Downgraded Legacy Encryption) est, à l’instar de DROWN, une attaque utilisant une version obsolète de SSL/TLS commme oracle de padding.
L’attaque POODLE utilise le fait que clients et serveurs acceptent de mener une downgrade dance : si le handshake avec TLSv1.2 est un echec, alors on se replie sur TLSv1.1 et ainsi de suite.
Un attquant en position de MitM peut amener client et serveur à utiliser SSLv3 et tirer parti de la présence d’un oracle de padding CBC dans ce protocole.
Oracle sur la longueur du pre_master_secret : Raccoon
Cette attaque datant de 2020 exploite un canal auxiliaire original : les octets nuls au début du pre_master_secret sont ignorés lorsque ce dernier est utilisé pour calculer le master_secret.
Cela signifie que quelque part dans l’implémentation du calcul du master_secret, un pointeur ptr pointant sur le début du pre_master_secret_ est incrémenté k fois, k étant le nombre d’octets nuls au début du pre_master_secret : La durée du handshake varie en fonction du nombre d’octets nuls au début du pre_master_secret.
Un attaquant dispose ainsi d’un oracle indiquant si le premier octet du pre_master_secret est nul, oracle qu’il peut interroger en initiant un handshake avec un g^x mod p de son choix.
Dans les grandes lignes, le scénario de l’attaque est le suivant :
- L’attaquant intercepte du trafic TLS entre un client et un serveur. Ce trafic débute par un handshake au cours duquel le serveur envoie un
ServerKeyExchangecontenant ung^b mod pet le client envoie ung^b mod pcontenu dans leClientKeyExchange. - L’attaquant souhaite retrouver le
pre_master_secretqui vautg^ab mod p. - Il initie des handshakes durant lesquels il utilise
g^ri * g^a mod pcomme demi-clé embarquée dans leClientKeyExchangeet ceux pour différentes valeurs deri - Ces valeurs aboutissent à des
pre_master_secretvalantg^(ri*b)* g^a*b) mod p, pour lesquels on connaît, grâce à l’oracle, la valeur de l’octet de poids fort. - L’attaquant peut ainsi construire un système d’équations lui permettant de retrouver
g^ab mod p.
L’attaque n’est possible que si le serveur réutilise le même logarithme b pour différentes connexions. C’est le cas si une suite Diffie-Hellman statique est employée, mais il a pu être observé que certains serveurs réutilisaient le même b pour plusieurs connexions, même en cas d’emploi de Diffie-Hellman ephémère.
L’attaque est décrite avec plus de détail sur le site qui lui est consacré :
https://raccoon-attack.com/
Les vulnérabilités d’environnement
Ces vulnérabilités ne sont pas des erreurs présentes dans TLS ou une implémentation de TLS à proprement parler : elles ont pour origine la manière dont est utilisée TLS dans les différentes logiciels faisant intervenir TLS – les navigateurs web, typiquement.
Les mécanismes de fallback
En cas d’echec du handshake et dans un souci d’interopérabilité, de nombreux clients TLS tentaient un handshake avec une version de TLS inférieure à celle initialement utilisée et pouvaient se retrouver à utiliser TLSv1.0 ou SSLv3.0.
Ce comportement pouvait être utilisé par un attaquant, qui, en injectant des TCP FIN ou RST pouvaient provoquer un repli vers version plus vulnérable.
La RFC 7507 décrit un mécanisme permettant de détecter des replis illégitimes.
Le principe est le suivant :
- Lorsqu’il réalise un fallback (c’est-à-dire lorsqu’il initie un handshake pour une version qui n’est pas la plus haute version de TLS qu’il supporte), le client inclut dans son
ClientHellola pseudo-suite cryptographiqueTLS_FALLBACK_SCSV. - Dans ces circonstances, le fallback n’est légitime que s’il est à l’initiative du serveur, auquel cas la version de TLS proposée dans le
ClientHelloest égale à la version maximale supportée par le serveur. Si ce n’est pas le cas, le fallback n’est pas légitime et le serveur interrompt le handshake en émettant une alarmeinappropriate_fallback.
SSL stripping et HSTS
Le SSL stripping n’est pas à proprement parler une vulnérabilité dans TLS, mais plutôt une faiblesse dans la logique suivie par un client pour ouvrir une session avec un serveur supportant TLS.
À la fin des années 2000, il était courant qu’un site web soit simultanément accessible en clair (http://www.lapin.com, port 80) en en chiffré (https://www.lapin.com, port 443).
Lorsqu’un client contactait le serveur sur le port 80, ce dernier lui retournait un redirect 302 le redirigeant vers le serveur https.
Un attaquant en position de MitM peut alors modifier à la volée cette réponse HTTP pour rediriger la victime vers une copie du site légitime. Cette victime interagira alors en clair avec le faux site, divulguant ses identifiants de session au passage.
Si le site web en question est uniquement accessible via HTTPS, l’attaque reste possible, au prix d’un peu de social engineering : l’attaquant amène la victime à contacter http://www.lapin.com, qu’il maîtrise et intercepte la communication du client.
L’en-tête HSTS (Strict-Transport-Security) empêche cette vulnérabilité, en permettant à www.lapin.com de spécifier au navigateur du client qu’il doit toujours utiliser HTTPS pour se connecter à www.lapin.com.
Les vulnérabilités d’implémentation
PRNG de la version debian d’OpenSSL
En 2006, l’équipe en charge de l’integration de OpenSSL dans debian a accidentellement commenté quelques lignes de code ayant un rôle critique dans le PRNG d’OpenSSL.
L’équipe en question utilisait l’outil Valgrind pour traquer les fuites mémoires et l’utilisation de zones mémoire non initialisées. L’une des remontées de l’outil portait sur une ligne alimentant le PRNG en aléa : une fois cette ligne commentée, la seule source d’aléa du PRNG était le pid du processus appelant…
Les deux lignes supprimées contiennent un appel à la fonction MD_Update :
247:
MD_Update(&m,buf,j);
467:
#ifndef PURIFY
MD_Update(&m,buf,j); /* purify complains */
#endif
L’erreur fut découverte en 2008 : toutes les clés générées avec OpenSSL sur debian étaient à jeter !
Erreur de validation du certificat : \0 dans le certificat
Lors de l’édition 2009 de Black Hat, le chercheur Moxie Marlinspike a présenté une vulnérabilité affectant la vérification du certificat serveur de plusieurs implémentations de TLS : la comparaison entre le nom de domaine contacté et le commonName du certificat s’arrête dès qu’un caractère \0 est rencontré.
Un attaquant peut ainsi forger un certificat pour www.paypal.com\0ssl.secureconnection.cc qui sera interprété comme un certificat valide pour www.payal.com.
Source : https://www.theregister.com/2009/10/05/fraudulent_paypay_certificate_published/
Erreur de validation du certificat : goto fail
Le nom de cette vulnérabilité (ayant fait l’objet de la CVE-2014-1266) le coté client de la pile TLS utilisée dans iOS et macOS provient d’une instruction surnuméraire ayant pour effet d’omettre toute une série d’étapes de la vérification d’un certificat.
À chaque étape, en cas d’echec le flux d’exécution est dérouté vers le label fail, et le code d’erreur err est retourné.
On voit que le deuxième goto fail est suivi d’un autre goto fail, qui lui est toujours exécuté.
static OSStatus
SSLVerifySignedServerKeyExchange(SSLContext *ctx, bool isRsa, SSLBuffer signedParams,
uint8_t *signature, UInt16 signatureLen)
{
OSStatus err;
...
if ((err = SSLHashSHA1.update(&hashCtx, &serverRandom)) != 0)
goto fail;
if ((err = SSLHashSHA1.update(&hashCtx, &signedParams)) != 0)
goto fail;
goto fail;
if ((err = SSLHashSHA1.final(&hashCtx, &hashOut)) != 0)
goto fail;
...
fail:
SSLFreeBuffer(&signedHashes);
SSLFreeBuffer(&hashCtx);
return err;
}
Ainsi, la fonction SSLVerifySignedServerKeyExchange retourne un code d’erreur signifiant « tout va bien » et les vérifications ultérieures ne sont pas effectuées : Un certficat serveur invalide est accepté !
Une vulnérabilité similaire (CVE-2014-009) a affecté gnuTLS la même année.
Heartbleed
Heartbleed (CVE-2014-0160) est une vulnérabilité dans l’implémentation OpenSSL du mécanisme de heartbeat.
C’est une vulnérabilité permettant de lire dans la mémoire du serveur TLS : ce n’est pas une vulnérabilité cryptographique.
Ce mécanisme de heartbeat est une extension de TLS décrite dans la RFC 6520.
Elle permet à chaque extremité d’une connexion TLS de s’assurer que l’autre extremité est encore vivante, ou de découvrir la Path MTU.
Pour s’assurer que le serveur TLS est vivant, le client lui envoie une heartbeat request. Cette heartbeat request consiste en un mot (« cheval »), précédé de sa longueur (6, dans notre cas).
Le serveur est censé répondre par une heartbeat response, contenant une copie exacte du buffer envoyé dans la request, en tenant compte de la longueur annoncée.
Le coeur de la vulnérabilité est que l’implémentation OpenSSL ne faisait pas de contrôle de cohérence entre d’une part la longueur annoncée et la taille réelle de la heartbeat request.
Ainsi, alors qu’une heartbeat request légitime est censée avoir cette structure :
+---------+--------+--------+
| en-tête | 0x0006 | cheval |
+---------+--------+--------+
fixe 2 6
<---- 6 octets + cste ------>
L’implémentation OpenSSL ne detectait aucun problème lorsqu’elle recevait ceci :
+---------+--------+--------+
| en-tête | 0xffff | cheval |
+---------+--------+--------+
fixe 2 6
<---- 6 octets + cste ------>
Et retournait 65535 octets dans sa heartbeat response, en lisant dans la mémoire du serveur au passage !
Erreur de machine à état : EarlyCCS
Cette attaque exploite une erreur dans l’implémentation OpenSSL de l’automate d’état sous-jacent au handshake TLS.
Lorsqu’un ChangeCipherSpec est envoyé au serveur après l’envoi du ServerHello et avant la génération du master_secret, la dérivation des clés de session est réalisée avec un master_secret uniquement constitué de zéros, permettant à un attaquant de totalement déchiffrer le trafic.
Cette vulnérabilité touche les versions de OpenSSL antérieures à la version 1.0.1h.
Erreur de machine à état : Freak
L’attaque FREAK, découverte en 2015 (« Factoring Rsa Export Keys ») exploite le support des suites EXPORT et des faiblesses dans l’automate d’état de certaines implémentations de TLS.
Cette attaque est réalisable lorsqu’un client supporte ces suites EXPORT et qu’un serveur accepte un ClientHello proposant une suite EXPORT.
L’attaquant remplace à la volée le ClientHello du client par un ClientHello proposant l’utilisation d’une suite EXPORT.
Le serveur envoie alors une clé RSA de 512 bits, avec laquelle le client chiffre le pre_master_secret.
L’attaquant, qui a préalablement récupéré et factorisé cette clé (ce qui réalisable pour un coût tout à fait modique aujourd’hui) déchiffre le pre_master_secret et peut dès lors réaliser la dérivation de clés de session, recalculer le message Finished et déchiffrer le trafic applicatif.
Erreur de machine à état : Logjam
L’attaque Logjam est au logarithme discret ce que Freak est à RSA : un attaquant en position de MitM remplace le ClientHello du client par un ClientHello proposant l’utilisation d’une suite DHE_EXPORT.
Finalement, client et serveur réalisent un échange Diffie-Hellman dans un groupe de 512 bits, dans lequel il calculatoirement possible de retrouver le logarithme discret.
L’attaquant peut ainsi retrouver le pre_master_secret, calculer le message Finished et déchiffrer le trafic applicatif.
Erreur de machine à état : SKIP-TLS
Plusieurs implémentations (JSSE, CyaSSL, OpenSSL…) de TLS autorisent des transitions d’état laxistes lors d’un handshake.
Par exemple dans JSSE, un attaquant pouvait dropper certains messages, ce qui aboutissait à la suite d’événements suivants :
- Le client envoie un
ClientHello, - Le serveur envoie un
ServerHello, - Le serveur envoie un
ServerKeyExchange, que l’attaquant détruit, - Le serveur envoie un
ServerHelloDone, que l’attaquant détruit, - Le client envoie un
ClientKeyExchange, que l’attaquant détruit, - Le client envoie un
ChangeCipherSpec, que l’attaquant détruit, - Le client envoie un
Finished, que l’attaquant détruit, - Le serveur envoie un
ChangeCipherSpec, que l’attaquant détruit, - Le serveur envoie un
Finished.
Ainsi, client et serveur se retrouvent à échanger du trafic applicatif chiffré et authentifié à partir de clés de session dérivées d’un pre_master_secret nul !
Sources
- Les différents articles et sites mentionnés tout au long du document
- wikipedia !
- Conférence de Olivier Levillain à l’édition 2012 du SSTIC (https://www.sstic.org/2012/presentation/ssl_tls_soa_recos/)
- Conférence de Olivier Levillain à l’édition 2015 du SSTIC (https://www.sstic.org/2015/presentation/ssltls_soa_reloaded/)
- Une excellente série de vidéos sur TLS (https://www.youtube.com/@cyrillgossi/videos)
- Le blog https://blog.cryptographyengineering.com
- Le site https://www.imperialviolet.org
- Le site https://www.mitls.org de l’équipe à l’origine des attaques FREAK, Logjam et SKIP-TLS
- La RFC 2246 (TLSv1.0)
- La RFC 4346 (TLSv1.1)
- La RFC 5256 (TLSv1.2)
- La RFC 5869 (RFC décrivant les fonctions de dérivation utilisées dans TLSv1.3)
- La RFC 6101 (RFC historique décrivant SSLv3)
- La RFC 6176 (retrait de SSLv2)
- La RFC 7366 (extension Encrypt-Then-Mac)
- La RFC 7457 (compilant les attaques connues sur SSL/TLS)
- La RFC 7507 (décrivant la pseudo-suite de chiffrement SCSV destinée à détecter les replis de version abusifs)
- La RFC 7465 (retrait de RC4)
- La RFC 8846 (TLSv1.3)
- « A Cryptographic Analysis of the TLS 1.3 Handshake Protocol », https://eprint.iacr.org/2020/1044.pdf