GnuTLS ist eine freie Implementierung von SSL- und TLS-Protokollen zum Aufbau von verschlüsselten Netzwerkverbindungen, die ein einfaches C-API bereitstellt. Wer einen TLS-Client programmieren möchte findet in der GnuTLS-Dokumentation zahlreiche Beispiele, die dies erklären. Wie sich kürzlich herausstellte, können Abweichungen von der Dokumentation zu ungewollten Sicherheitslücken führen. Ursache ist ein Buffer Overflow, der beim Wiederaufbau von älteren Verbindungen auftreten kann.
Der Overflow befindet sich in der Implementierung der Funktion “gnutls_session_get_data()”, die zum Session-Resuming vorgesehen ist, also zum Fortsetzen einer älteren TLS-Verbindung. Hierzu liefert “gnutls_session_get_data()” alle relevanten Session-Parameter zurück, wodurch die Software die alte Session mit “gnutls_session_set_data()” fortsetzen kann. Die GnuTLS-Dokumentation demonstriert Resuming an einem Code-Beispiel.
Der relevante Teil zum Fortsetzen der Verbindung befindet sich in den folgenden Code-Zeilen:
/* get the session data size */ gnutls_session_get_data (session, NULL, &session_data_size); session_data = malloc (session_data_size); /* put session data to the session variable */ gnutls_session_get_data (session, session_data, &session_data_size);
Der Code fragt die Session-Information zunächst mit “gnutls_session_get_data()” ab. Diese Funktion ruft er zwei Mal auf: das erste Mal, um die benötigte Größe für den Session-Buffer in Erfahrung zu bringen und das zweite Mal, um diesen Bufferbasierend auf dieser Größe zu allozieren. “gnutls_session_get_data()” liefert dabei die Größe des Buffers genau dann zurück, wenn für den Session-Data-Buffer ein NULL-Zeiger übergeben wird. In diesem Fall wird in “session_data_size” die benötigte Größe abgelegt. Anderenfalls, kommt “session_data” als Buffer direkt zur Verwendung.
Laut Dokumentation ist der zweimalige Aufruf von “gnutls_session_get_data()” nach diesem Schema empfohlen. Allerdings ist es prinzipiell auch möglich, direkt einen Puffer zu allozieren und “gnutls_session_get_data()” nur einmal mit diesem Puffer aufzurufen. Natürlich sollte die GnuTLS-Bibliothek in diesem Fall prüfen, dass der übergebene Puffer auch groß genug ist, um alle Session-Daten zu speichern. Wie sich nun herausgestellt hat, befindet sich bei dieser Kontrolle ein Fehler im Programmcode. Der fehlerhafte Code sieht so aus:
*session_data_size = psession.size;
if (psession.size > *session_data_size) { ret = GNUTLS_E_SHORT_MEMORY_BUFFER; goto error; }
“*session_data_size” speichert dabei die vom Anwender übergebene Puffergröße, die mit der benötigte Größe “psession.size” verglichen werden muss. Dies soll die If-Abfrage erledigen. Falls der Puffer zu klein ist, bricht der Programmfluss via “goto error” aus der Funktion aus. Offensichtlich liefert diese If-Abfrage aber immer “false” und bemerkt somit nicht, wenn der Puffer zu klein ist (“GNUTLS_E_SHORT_MEMORY_BUFFER”). Der korrekte Code müsste so aussehen:
if (psession.size > *session_data_size) { ret = GNUTLS_E_SHORT_MEMORY_BUFFER; goto error; }
*session_data_size = psession.size;
Diese triviale Änderung fängt den Overflow korrekt ab und verhindert Attacken. Die Wahrscheinlichkeit, dass diese Schwachstelle ernsthafte Konsequenzen hat, ist recht gering, da die meisten Clients wohl der GnuTLS-Dokumentation folgen dürften. Ist dies allerdings nicht der Fall, so kann ein entfernter Angreifer mit Hilfe eines speziellen Servers den Client zum Absturz bringen. Der Server müsste hierzu sehr große Session-Tickets an den Client senden.
Betroffen sind die GnuTLS-Versionen 2.x vor 2.12.14 und 3.0 vor 3.0.7.

