FlawOpen

flawopen.com/Teardowns/cve-2014-0160-heartbleed

● CVE-2014-0160 · CVSS 7.5 · Haute
Recherche · FlawOpen

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]

💡 Explication en Langage Simple (ELI5)

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

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

Étape 1

Un pair envoie un enregistrement Heartbeat court

Un pair TLS ou DTLS envoie un message Heartbeat dont la longueur de charge utile déclarée dépasse les octets présents dans l’enregistrement.[1], [3]

Étape 2

OpenSSL fait confiance à la longueur déclarée

Avant le correctif, le handler lit le type et la longueur sans vérifier d’abord la taille minimale de l’enregistrement ni comparer la charge utile à l’enregistrement reçu.[1], [2]

Étape 3

La réponse copie au-delà de l’enregistrement

memcpy() copie le nombre d’octets déclaré depuis le tampon de l’enregistrement. La réponse peut inclure des octets après le message Heartbeat reçu, jusqu’à la limite de 64 Kio du protocole.[1], [2]

Étape 4

L’exposition dépend du contenu du processus

Le pair peut recevoir des octets de la mémoire du processus. La réponse ne garantit aucun secret précis ; le résultat dépend de la mémoire présente et de l’état du point de terminaison.[1], [4]

Étape 5

Le handler corrigé rejette l’incohérence

Le correctif vérifie l’en-tête et le padding avant l’analyse, confirme que la charge utile tient dans l’enregistrement reçu et borne la réponse avant la copie.[2], [1]

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

IMPLÉMENTATION VULNÉRABLE
/* 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);
PATCH SÉCURISÉ ET ROBUSTE
/* 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

2012-03-14

OpenSSL 1.0.1 est publié avec le traitement Heartbeat TLS/DTLS sans vérification de la longueur de l’enregistrement.[3], [1]

2014-04-07

OpenSSL publie l’avis CVE-2014-0160 et OpenSSL 1.0.1g ; l’avis remercie Neel Mehta de Google Security. Le correctif de la bêta 1.0.2 arrive dans la bêta2.[1], [2]

2014-04

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

Sources