flawopen.com/Teardowns/cve-2014-0160-heartbleed
CVE-2014-0160 : fuite mémoire Heartbleed
CVE-2014-0160 permettait à un pair TLS ou DTLS de lire jusqu’à 64 Kio au-delà d’un enregistrement Heartbeat lorsqu’OpenSSL faisait confiance à une longueur de charge utile absente de l’enregistrement. Le correctif amont valide l’enregistrement avant d’analyser et de renvoyer la charge utile.[1], [2]
Imaginez un employé qui reçoit une carte avec quelques mots, mais croit une étiquette demandant une copie beaucoup plus longue. Au lieu de s’arrêter au bord de la carte, il continue à copier le texte qui suit dans le bac partagé. Heartbleed faisait copier OpenSSL au-delà de l’enregistrement reçu ; les octets exposés dépendaient du contenu de la mémoire du processus.[1], [3]
Concepts Clés et Termes
Heartbeat TLS/DTLS (RFC 6520)- Message de maintien de connexion en TLS ou DTLS. La requête contient une charge utile que le pair renvoie, ainsi que le padding exigé par le protocole.[3]
payload_length- Champ de 16 bits indiquant la taille de la charge utile Heartbeat. Celle-ci doit tenir dans l’enregistrement reçu après le type, le champ de longueur et le padding minimal.[1], [3]
memcpy()- Copie un nombre d’octets donné depuis un pointeur source. La fonction ne connaît pas la longueur valide du tampon source ; l’appelant doit la vérifier.[1], [2]
Lecture au-delà du tampon (CWE-126)- Lecture après la fin du tampon source prévu. Dans Heartbleed, la réponse pouvait divulguer jusqu’à 64 Kio de mémoire du processus, selon son contenu.[5]
Correspondances de référentiels
- Référence CWE pour la faiblesse décrite dans ce guide : CWE-126
- CISA KEV — ajouté le 2022-05-04 — CVE-2014-0160
Analyse de Cause Racine
tls1_process_heartbeat() et dtls1_process_heartbeat() lisaient la longueur de charge utile sur 16 bits d’un message Heartbeat puis, dans le chemin de réponse, la transmettaient à memcpy() sans vérifier que ces octets existaient dans s->s3->rrec.length. Un pair pouvait déclarer plus de données que l’enregistrement TLS ou DTLS n’en contenait. OpenSSL renvoyait alors des octets situés après l’enregistrement reçu et pouvait divulguer jusqu’à 64 Kio de mémoire du processus ; le contenu dépendait de cette mémoire, sans garantir la présence d’une clé, d’un mot de passe ou d’un cookie précis. Le commit amont 96db902 rejette d’abord les enregistrements trop courts pour contenir le type, la longueur et le padding minimal, puis compare 1 + 2 + payload + 16 à la longueur de l’enregistrement reçu. Il borne aussi la réponse avec SSL3_RT_MAX_PLAIN_LENGTH et utilise le write_length calculé. Le même schéma de vérification a été appliqué aux handlers TLS et DTLS.[1], [2], [3], [4]
Déroulement de l'Attaque Étape par Étape
Code Source : Vulnérable vs Sécurisé
Ces extraits proviennent du changement amont d’OpenSSL et ne constituent pas un programme autonome. Le code vulnérable analyse les champs avant de vérifier la présence de l’en-tête fixe et du padding minimal, puis copie la longueur déclarée par le pair. Le correctif vérifie la taille minimale de l’enregistrement avant l’analyse, valide la charge utile déclarée par rapport à l’enregistrement reçu et borne la réponse. OpenSSL a appliqué des modifications correspondantes à tls1_process_heartbeat() et dtls1_process_heartbeat().[2], [1]
Extrait source: ssl/t1_lib.c
/* Extrait de tls1_process_heartbeat() avant le correctif. */
hbtype = *p++;
n2s(p, payload);
pl = p;
/* Dans le chemin de réponse TLS1_HB_REQUEST : */
memcpy(bp, pl, payload);
r = ssl3_write_bytes(s, TLS1_RT_HEARTBEAT,
buffer, 3 + payload + padding);
/* Rejette un enregistrement trop court pour le type, la longueur et le remplissage minimal. */
if (1 + 2 + 16 > s->s3->rrec.length)
return 0;
hbtype = *p++;
n2s(p, payload);
if (1 + 2 + payload + 16 > s->s3->rrec.length)
return 0;
pl = p;
/* Dans le chemin de réponse TLS1_HB_REQUEST : */
unsigned int write_length = 1 + 2 + payload + padding;
if (write_length > SSL3_RT_MAX_PLAIN_LENGTH)
return 0;
buffer = OPENSSL_malloc(write_length);
memcpy(bp, pl, payload);
r = ssl3_write_bytes(s, TLS1_RT_HEARTBEAT, buffer, write_length);
Chronologie de l'Incident
Les opérateurs corrigent les points de terminaison concernés et évaluent l’exposition possible. Heartbleed.com indique que les journaux habituels ne précisent pas quels octets mémoire une réponse a renvoyés ; après le correctif, le remplacement des clés, certificats, sessions et identifiants est évalué.[6], [1]
Liste de Contrôle de Sécurité pour l'Ingénierie
- ✓Passez à une version d’OpenSSL encore prise en charge par le fournisseur et contenant le correctif ; l’amont a corrigé 1.0.1g et 1.0.2-beta2. Vérifiez les rétroportages de la distribution au lieu de vous fier uniquement au numéro affiché.[1], [2]
- ✓Pour les points de terminaison potentiellement exposés, remplacez les clés privées TLS et certificats concernés et révoquez les anciens certificats si une exposition est plausible ; incluez les clients ayant traité des heartbeats non fiables.[6]
- ✓Après la mise à jour du service vulnérable, renouvelez les identifiants qui ont pu se trouver dans sa mémoire de processus. Ne changez les mots de passe utilisateur qu’après confirmation du responsable que le service est corrigé.[6], [8]
- ✓Testez les handlers TLS et DTLS aux limites de l’enregistrement : en-têtes fixes trop courts, longueurs de charge utile supérieures aux octets reçus et requêtes valides avec padding minimal.[2], [3]
- ✓Si Heartbeat est inutile et que la compatibilité le permet, recompilez avec
-DOPENSSL_NO_HEARTBEATS, mesure alternative mentionnée dans l’avis OpenSSL.[1]
Sources
- OpenSSL Security Advisory — TLS heartbeat read overrun (CVE-2014-0160)
- OpenSSL upstream fix — commit 96db902
- RFC 6520 — TLS and DTLS Heartbeat Extension
- NVD — CVE-2014-0160
- MITRE CWE-126 — Buffer Over-read
- Codenomicon / Heartbleed.com — The Heartbleed Bug
- CVE Program — CVE-2014-0160
- CISA — “Heartbleed” OpenSSL Vulnerability