FlawOpen

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

● CVE-2014-0160 · CVSS 7.5 · Hoch
Sicherheitsforschung · FlawOpen

CVE-2014-0160: Heartbleed-Speicherleck in OpenSSL

CVE-2014-0160 ermöglichte einem TLS- oder DTLS-Peer, bis zu 64 KiB hinter einem Heartbeat-Record zu lesen, wenn OpenSSL einer Payload-Länge vertraute, die der Record nicht enthielt. Der Upstream-Fix prüft den Record, bevor er die Payload auswertet und zurücksendet.[1], [2]

💡 Einfache Erklärung (ELI5)

Stellen Sie sich eine Person vor, die eine Karte mit wenigen Wörtern erhält, aber einem Etikett glaubt, das eine viel längere Kopie verlangt. Statt am Kartenrand anzuhalten, kopiert sie weiter den Text, der in der gemeinsamen Ablage folgt. Heartbleed ließ OpenSSL über den empfangenen Record hinaus kopieren; welche Bytes sichtbar wurden, hing vom Prozessspeicher ab.[1], [3]

Kernkonzepte & Begriffe

TLS/DTLS-Heartbeat (RFC 6520)
Keep-alive-Nachricht in TLS oder DTLS. Eine Anfrage enthält eine Payload, die der Peer zurückspiegelt, sowie das vorgeschriebene Padding.[3]
payload_length
16-Bit-Feld für die Größe der Heartbeat-Payload. Sie muss nach Nachrichtentyp, Längenfeld und Mindest-Padding in den empfangenen Record passen.[1], [3]
memcpy()
Kopiert eine angegebene Bytezahl ab einem Quellzeiger. Die Funktion kennt die gültige Länge des Quellpuffers nicht; der Aufrufer muss sie prüfen.[1], [2]
Pufferüberlesen (CWE-126)
Lesen hinter dem Ende des vorgesehenen Quellpuffers. Bei Heartbleed konnte die Antwort je nach Speicherinhalt bis zu 64 KiB Prozessspeicher offenlegen.[5]

Framework-Zuordnungen

Ursachenanalyse

tls1_process_heartbeat() und dtls1_process_heartbeat() lasen die 16-Bit-Payload-Länge einer Heartbeat-Nachricht und übergaben sie im Antwortpfad an memcpy(), ohne nachzuweisen, dass diese Payload-Bytes in s->s3->rrec.length vorhanden waren. Ein Peer konnte mehr Daten angeben, als der TLS- oder DTLS-Record enthielt. OpenSSL spiegelte dann Bytes hinter dem empfangenen Record zurück und konnte bis zu 64 KiB Prozessspeicher offenlegen; der Inhalt hing vom Speicher ab, daher war ein bestimmter Schlüssel, ein Passwort oder Cookie nicht garantiert. Der Upstream-Commit 96db902 verwirft zuerst Records, die für Typ, Längenfeld und Mindest-Padding zu kurz sind, und vergleicht anschließend 1 + 2 + payload + 16 mit der Länge des empfangenen Records. Außerdem begrenzt er die Antwort mit SSL3_RT_MAX_PLAIN_LENGTH und verwendet das berechnete write_length. Dasselbe Prüfmuster wurde auf TLS- und DTLS-Handler angewandt.[1], [2], [3], [4]

Schritt-für-Schritt Angriffsablauf

Schritt 1

Ein Peer sendet einen kurzen Heartbeat-Record

Ein TLS- oder DTLS-Peer sendet eine Heartbeat-Nachricht, deren angegebene Payload-Länge größer ist als die im Record vorhandenen Payload-Bytes.[1], [3]

Schritt 2

OpenSSL vertraut der angegebenen Länge

Vor dem Fix liest der Handler Typ und Länge, ohne zuerst die Mindestgröße des Records zu prüfen oder die Payload mit dem empfangenen Record abzugleichen.[1], [2]

Schritt 3

Die Antwort kopiert über den Record hinaus

memcpy() kopiert die angegebene Bytezahl aus dem Record-Puffer. Die Antwort kann Bytes hinter der empfangenen Heartbeat-Nachricht enthalten, bis zur 64-KiB-Protokollgrenze.[1], [2]

Schritt 4

Die Offenlegung hängt vom Prozessspeicher ab

Der Peer kann Bytes aus dem Prozessspeicher erhalten. Die Antwort garantiert kein bestimmtes Geheimnis; das Ergebnis hängt vom vorhandenen Speicher und dem Zustand des Endpunkts ab.[1], [4]

Schritt 5

Der gepatchte Handler verwirft den Widerspruch

Der Fix prüft Header und Padding vor dem Parsen, bestätigt, dass die Payload in den empfangenen Record passt, und begrenzt die Antwort vor dem Kopieren.[2], [1]

Quellcode: Verwundbar vs. Sicher

Diese Ausschnitte stammen aus der OpenSSL-Upstream-Änderung und sind kein eigenständiges Programm. Der verwundbare Code verarbeitet die Felder, bevor er prüft, ob der feste Header und das Mindest-Padding vorhanden sind, und kopiert danach die vom Peer angegebene Payload-Länge. Der Fix prüft die Mindestgröße des Records vor dem Parsen, validiert die angegebene Payload gegen den empfangenen Record und begrenzt die Antwort. OpenSSL nahm entsprechende Änderungen an tls1_process_heartbeat() und dtls1_process_heartbeat() vor.[2], [1]

Quelltextauszug: ssl/t1_lib.c

VERWUNDBARE IMPLEMENTIERUNG
/* Auszug aus tls1_process_heartbeat() vor der Korrektur. */
hbtype = *p++;
n2s(p, payload);
pl = p;

/* Im Antwortpfad für TLS1_HB_REQUEST: */
memcpy(bp, pl, payload);
r = ssl3_write_bytes(s, TLS1_RT_HEARTBEAT,
                     buffer, 3 + payload + padding);
GEHÄRTETER SICHERHEITS-PATCH
/* Weist einen Datensatz zurück, der für Typ, Länge und Mindestauffüllung zu kurz ist. */
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;

/* Im Antwortpfad für 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);

Vorfall-Zeitstrahl

2012-03-14

OpenSSL 1.0.1 erscheint mit TLS/DTLS-Heartbeat-Verarbeitung ohne Prüfung der Record-Länge.[3], [1]

2014-04-07

OpenSSL veröffentlicht den Hinweis zu CVE-2014-0160 und 1.0.1g; der Hinweis dankt Neel Mehta von Google Security. Der Fix für die 1.0.2-Beta folgt mit Beta2.[1], [2]

2014-04

Betreiber patchen betroffene Endpunkte und bewerten eine mögliche Offenlegung. Heartbleed.com weist darauf hin, dass gewöhnliche Protokolle nicht festhalten, welche Speicherbytes eine Antwort zurückgab; nach dem Patch wird der Austausch von Schlüsseln, Zertifikaten, Sitzungen und Zugangsdaten geprüft.[6], [1]

Checkliste für Engineering & Systemsicherheit

Quellen