Auf erstaunlich einfache Art und Weise können Angreifer wichtige Daten wie Passwörter und geheime Schlüssel von einem Server stehlen - nur weil ein einziger Check vergessen wurde.

Der sogenannte Heartbeat soll es eigentlich Server und Client ermöglichen, eine TLS-Verbindung am Leben zu halten. Zu diesem Zweck sendet einer der Kommunikationspartner eine Payload mit beliebigem Inhalt an das andere Ende. Der Kommunikationspartner schickt dann exakt die selben Daten zurück, um zu zeigen, dass die Verbindung nach wie vor in Ordnung ist.

Wenn der Client über die Länge lügt, füllt der Server das Antwortpaket großzügig mit eigenen Daten auf.

Das Problem bei der Umsetzung der TLS-Heartbeat-Funktion in OpenSSL war, dass das Programm nicht überprüft, wie lang die empfangene Payload tatsächlich ist – der Empfänger glaubt dem Absender einfach. Der kann in das dafür vorgesehene Feld payload_length im Header des Payload-Paketes beliebige Werte schreiben. Lügt der Absender bei der Größe der Payload, kann er letztlich Speicher der Gegenstelle auslesen. Dieser Heartbleed-Angriff funktioniert in beide Richtungen, aber im Folgenden sei angenommen, dass ein böser Client einen verwundbaren Server angreift.

Der Angriff

Konkret funktioniert ein Ausnutzen der Lücke so: Der Angreifer schickt dem Server eine Heartbeat-Payload von einem Byte Größe, behauptet aber, sie sei beispielsweise 16 KByte groß. Der Server schreibt das Byte des Angreifers in seinen Speicher in einen Puffer namens pl . Da die eigentliche Größe der Payload nicht verglichen wird, geht der Server beim Zurücksenden der Payload von der vom Angreifer angegebenen Größe aus ( payload ). Er reserviert also 16 KByte Speicher (plus ein bisschen Platz für Verwaltungsinformationen)

buffer = OPENSSL_malloc(1 + 2 + payload + padding);

bp = buffer;

und kopiert dann für die Antwort die vom Angreifer angegebene Payload-Größe (mehrere KByte) an diese Stelle:

memcpy(bp, pl, payload);

An der Quelle des Kopiervorgangs pl steht aber nur ein Byte aus dem eingehenden Heartbeat. Der darauf folgende Bereich ist mit irgendwelchen anderen Daten belegt, die der Server gerade bearbeitet und werden in einem Rutsch mit kopiert.

Erschwerend hinzu kommt, dass OpenSSL seine eigene Speicherverwaltung implementiert (siehe OPENSSL_malloc() ), so dass dort dann nicht irgendwelche allgemeinen Daten des Servers stehen, sondern Daten aus dem Kontext von OpenSSL. Das sind dann häufig Passwörter oder andere, gerade eben entschlüsselte Daten eines anderen Benutzers oder auch geheime Schlüssel des Servers. Und dieses komplette Datenpaket bp sendet der Server dann zurück an den Angreifer. Diesen Angriff kann man nun beliebig wiederholen und so massenweise Daten blockweise auslesen.

Die Spezifikation des Heartbeat-Protokolls gibt als maximale Payload-Größe 16 KByte an (RFC6520: The total length of a HeartbeatMessage MUST NOT exceed 2^14). Einige der Exploit-Programme lesen auch nur 16 KByte große Stücke des Speichers aus. Allerdings überprüft OpenSSL dies ebenfalls nicht, so dass ein Angreifer bis zu 64 KByte auf einen Rutsch auslesen kann, indem er den maximalen Wert 0xFFFF als Payload-Größe angibt.

Der Patch

Der Fehler steckt in der Funktion tls1_process_heartbeat der Datei t1_lib.c der OpenSSL-Bibliothek. Dort haben die Entwickler jetzt einen zusätzlichen Check eingebaut:

if (1 + 2 + payload + 16 > s->s3->rrec.length)

return 0; /* silently discard per RFC 6520 sec. 4 */

Hier wird die Länge der Variable payload mit der tatsächlichen Größe der über die SSL3_RECORD-Struktur ( s3->rrec ) empfangenen Payload verglichen. Ist diese zu groß angegeben, verwirft der Server das Paket nun, ohne dem Angreifer zu antworten.

Das besondere am Heartbleed-Angriff ist, dass hier keine fremden Speicherbereiche überschrieben werden, was unter Umständen zu einem Programmabsturz führen könnte. Statt dessen schießt lediglich ein Lesezugriff etwas übers Ziel hinaus. Damit gefährdet der Angriff die Stabilität des Servers in keiner Weise und hinterlässt leider auch keinerlei Spuren. (fab)



Update 11:30, 11.4.2014: Größe des maximal möglichen Speicherblocks korrigiert. Ursprünglich waren als Obergrenze die in der Originaldokumentation des Heartbleed-Bugs aufgeführten 64 KByte angegeben. Die RFCs und auch die Demo-Exploits lassen jedoch nur maximal 16 KByte zu

2. Update 11:19, 14.04.2014: Erneute Korrektur der maximalen Palyoad-Größe: OpenSSL checkt auch diese Größe nicht, sodass tatsächlich die von den Entdeckern der Lücke angegebenen 64 KByte möglich sind.