Skip to content

Der Code sagte GOTO FAIL, und Apples SSL-Prüfung gehorchte

Apple hat ein dringendes Update für iOS veröffentlicht, die damit behobenen Schwachstelle in der Prüfung von SSL-Zertifikaten betrifft auch Mac OS X. Der zu Grunde liegende Fehler ist... sagen wir mal "interessant", und das aus mehreren Gründen.

Mit einem iOS-Update ging es los...

Mit dem Update auf iOS 6.1.6 und 7.0.6 sowie Apple TV 6.0.2 wurde die Schwachstelle mit der CVE-ID CVE-2014-1266 behoben. Apples Beschreibung dazu lautet

"Impact: An attacker with a privileged network position may capture or modify data in sessions protected by SSL/TLS

Description: Secure Transport failed to validate the authenticity of the connection. This issue was addressed by restoring missing validation steps."

Das klingt schon mal gar nicht gut. Die Frage ist nur, was ein Angreifer mit einer "privileged network position" sein soll. Wie sich herausgestellt hat, zum Beispiel ein Man-in-the-Middle. Das ist die NSA ja gerne mal, in offenen WLANs kann sich ein MitM leicht in die Verbindung einschleichen, und dann gibt es ja des öfteren Probleme mit SOHO-Routern, die schnell dazu führen können, dass ein MitM es sich im Router gemütlich macht. Mit anderen Worten: Diese Schwachstelle gehört in der Tat zu den schlimmsten, die es im Fall von SSL geben kann.

Katze im offenen Sack

Da Apple den betroffenen Socurcecode veröffentlicht, dauerte es nicht lange, bis jemand auf den Fehler hin wies. Nachdem die Katze damit aus dem Sack war, hat Adam Langley sich die Mühe gemacht, das Problem zu erklären. Was eigentlich gar nicht nötig ist, denn wenn man weiß, nach was man suchen muss, erklärt sich das Problem eigentlich von selbst. Hier ist der relevante Teil des Sourcecodes, den Fehler habe ich rot hervor gehoben:

static OSStatus
SSLVerifySignedServerKeyExchange(SSLContext *ctx, bool isRsa, SSLBuffer signedParams,
                                 uint8_t *signature, UInt16 signatureLen)
{
	OSStatus        err;
	...

	if ((err = SSLHashSHA1.update(&hashCtx, &serverRandom)) != 0)
		goto fail;
	if ((err = SSLHashSHA1.update(&hashCtx, &signedParams)) != 0)
		goto fail;
		goto fail;
	if ((err = SSLHashSHA1.final(&hashCtx, &hashOut)) != 0)
		goto fail;

	err = sslRawVerify(ctx,
                       ctx->peerPubKey,
                       dataToSign,				/* plaintext */
                       dataToSignLen,			/* plaintext length */
                       signature,
                       signatureLen);

	...

fail:
	SSLFreeBuffer(&signedHashes);
	SSLFreeBuffer(&hashCtx);
	return err;
}

Ursache des Problems sind die zwei "goto fail;"-Zeilen. Die erste ist an die if-Abfrage gebunden und wird nur ausgeführt, wenn die if-Bedingung erfüllt ist. Die zweite, von mir rot markierte, aber führt dazu, dass der Code immer nach fail: springt, wenn er die Anweisung erreicht. Die eigentliche Prüfung der Signatur durch die Funktion sslRawVerify() wird also niemals durchgeführt. Und da SSLHashSHA1.update() beim Erreichen der "goto fail;"-Zeile bereits erfolgreich war (sonst wäre schon aus einer der if-Anweisungen davor zu fail: gesprungen worden), enthält err einen erfolgreichen Wert und die Signaturprüfung durch SSLVerifySignedServerKeyExchange() schlägt niemals fehl.

Die Funktion SSLVerifySignedServerKeyExchange() prüft die Signatur einer ServerKeyExchange-Nachricht, die von DHE- und ECDHE-Verschlüsselungsverfahren verwendet wird, um dem Client den Sitzungsschlüssel mitzuteilen. Die Nachricht enthält den Schlüssel und die zugehörige Signatur des Servers, mit der der Server beweist, dass der Schlüssel auch wirklich von ihm stammt und nicht von einem Man-in-the-Middle eingeschleust wurde.

Da die Signaturprüfung im Grunde nicht statt findet, kann ein Man-in-the-Middle seinen eigenen Sitzungsschlüssel einschleusen. Er muss nur ein Zertifikat mit einer korrekten Zertifikatskette bis zu einem vertrauenswürdigen Herausgeber an den Client senden, den selbst erzeugten Schlüssel in der ServerKeyExchange-Nachricht kann er mit irgend einem vom Zertifikat unabhängigen privaten Schlüssel oder auch gar nicht signieren - die Signatur wird ja gar nicht geprüft. Der Client verwendet dann diesen Schlüssel, und der MitM kann die Kommunikation nach belauschen und nach Belieben manipulieren.

Besonders unschön ist, dass viele Schutzmaßnahmen in diesem Fall nicht greifen:

  • Beim Certificate Pinning besteht der Webbrowser auf einem bestimmten Zertifikat. Das Zertifikat interessiert den MitM aber gar nicht, er kann einfach das Originalzertifikat durch reichen. Er muss in der ServerKeyExchange-Nachricht nur den vom Server erzeugten und signierten Session-Schlüssel durch seinen eigenen Session-Schlüssel mit seiner Signatur oder ohne Signatur ersetzen.
  • Versuchen Client oder Server TLS 1.2 zu verwenden, dass eine andere Funktion zum Prüfen der ServerKeyExchange-Nachricht verwendet und nicht von der Schwachstelle betroffen ist, kann der MitM versuchen, SSL aus zu handeln. Sofern Client und Server sich darauf einlassen, ist der Angriff möglich. Der Schutz ist also nur erfolgreich, wenn der Client ausschließlich TLS 1.2 verwendet und ansonsten auf den Verbindungsaufbau verzichtet. Was unpraktisch ist, wenn auf der Gegenseite ein Server steht, der TLS 1.2 einfach nicht unterstützt.
  • Das gleiche gilt, wenn nicht betroffene Cipher-Suiten verwendet werden sollen - solange der MitM eine der betroffenen Suiten aushandeln kann, ist der Angriff möglich.
  • Nur wenn der Client ausschließlich die reinen RSA-Cipher-Suiten verwendet ist kein Angriff möglich, da es dann keinen ServerKeyExchange gibt. Dafür ist aber keine Verbindung mit Servern möglich, die diese Chiffren nicht unterstützen.

Adam Langley hat eine Testsite eingerichtet, mit der Sie prüfen können, ob Ihr Rechner von der Schwachstelle betroffen ist: https://www.imperialviolet.org:1266/. An diesem Port sendet der Server sein korrektes SSL-Zertifikat, signiert die ServerKeyExchange-Nachricht aber mit einem komplett anderen Schlüssel. Wenn alles in Ordnung ist, muss es eine Fehlermeldung wie zum Beispiel

"Safari kann die Seite "https://www.imperialviolet.org:1266/" nicht öffnen, da Safari keine sichere Verbindung zum Server "https://www.imperialviolet.org:1266/" aufbauen kann."

geben, da die Signaturprüfung fehl schlägt. Wenn Sie von diesem Port eine HTTPS-Website laden können, wurde die falsche Signatur akzeptiert, Ihr System enthält also den Fehler.

Update 25.2.:
Aldo Cortesi hat sein Tool mitmproxy so angepasst, dass die Schwachstelle damit ausgenutzt werden kann.
Ende des Updates

Mac OS X Mavericks ist auch betroffen

Das aktuelle Mac OS X Mavericks enthält die Schwachstelle ebenfalls, einen Patch hat Apple bereits angekündigt. Besser wäre es allerdings gewesen, alle Updates auf einmal auszuliefern.

Update 25.2.:
Apple hat OS X Mavericks 10.9.2 und das Security Update 2014-001 veröffentlicht, dass unter anderem die "GOTO FAIL"-Schwachstelle behebt.
Ende des Updates

GOTO ist nicht harmlos!?

Spätestens seit Edsger W. Dijkstra berühmten Text "Letters to the editor: Go To Statement Considered Harmful" in den "Communications of the ACM" 11, Nr. 3, März 1968 (als PDF und Text) sollte man bei Entwicklern voraus setzen, dass Sie beim Einsatz des GOTO-Befehls vorsichtig sind. Diese Fehler sieht mir auf den ersten Blick sehr nach einem Lösch- oder Copy&Paste-Fehler aus - entweder wurde eine if-Abfrage raus geschnitten und die zugehörige "goto fail;"-Zeile vergessen, oder beim rein kopieren der "goto fail;"-Zeile wurde zwei mal eingefügt. Hätte man sich den entstandenen Code angesehen, hätte dieser Fehler auffallen müssen. Oder zumindest sollen. Und bei sicherheitsrelevanten Code wie diesem sollte man eigentlich davon ausgehen, dass jede Code-Änderung von einem zweiten Entwickler geprüft wird.

Klammert gefälligst!

Andererseits ist das eigentliche Problem nicht das GOTO, der gleiche Fehler wäre auch entstanden, wenn die Freigabe der Puffer und die Rückgabe des Fehlercodes in einer Funktion erledigt würde und da statt goto fail; zum Beispiel return cleanup(err); gestanden hätte. Das oder zumindest ein Problem ist m.E. die Verwendung von if-Abfragen ohne Code-Block. Um es brutal zu formulieren: Wäre anständig geklammert worden, wäre es sehr wahrscheinlich nie zu diesem Fehler gekommen. Aber wenn man es sich einfach macht, macht man es eben auch dem Entstehen von Fehlern und damit Schwachstellen einfach.

Nehmen wir mal an, die "goto fail;"-Zeile ist versehentlich in die if-Abfrage eingefügt worden. Dann stünde da

	...

	if ((err = SSLHashSHA1.update(&hashCtx, &serverRandom)) != 0) {
		goto fail;
   }
	if ((err = SSLHashSHA1.update(&hashCtx, &signedParams)) != 0) {
		goto fail;
		goto fail;
   }
	if ((err = SSLHashSHA1.final(&hashCtx, &hashOut)) != 0) {
		goto fail;
   }
	...
}

und die zusätzliche GOTO-Anweisung hätte keinerlei Auswirkung auf dem Programmablauf. Jedenfalls wenn der, der sie eingefügt hat, sie in die if-Abfrage einfügen wollte.

Wobei es natürlich auch sicher unendlich viele Möglichkeiten gibt, den gleichen Fehler mit Klammerung zu erzeugen. Womit wir bei der entscheidenden Frage angekommen sind: Ist das ein zufällig entstandener Fehler - oder eine absichtlich eingefügte und als "Fehler" getarnte Backdoor? Denn die würde man genau als solche Art von Fehler tarnen.

Wo kommt der Fehler her?

Alex Yakoubian hat ein Diff der Dateien aus Mac OS X 10.8.5 (Security-55179.13) und 10.9 (Security-55471) erzeugt. Die betroffene Zeile 631 wurde anscheinend ziemlich unmotiviert hinzugefügt, es gibt zumindest keinen ersichtlichen Grund dafür, warum sie eingefügt wurde. Die anderen Code-Änderungen in der Funktion SSLVerifySignedServerKeyExchange() scheinen mit dieser zusätzlichen Zeile nichts zu tun zu haben. Vielleicht hat man bei Apple ja den Grund für diese Code-Änderung dokumentiert und verrät ihn uns irgendwann.

Fehler oder Hintertür?

Die Zeile wurde gezielt eingefügt. Die Frage ist eben nur, wieso. Und so lange Apple sich nicht dazu äußert, kann man nur spekulieren. Und da man nichts mit Boshaftigkeit erklären soll, was sich auch durch Dämlichkeit erklären lässt, und außerdem "Im Zweifel für den Beschuldigten" gelten sollte, sollten wir erst mal von einem ziemlich dummen Fehler ausgehen. Der, der das eingefügt hat, dürfte zur Zeit einige unangenehmen Fragen beantworten müssen und noch unangenehmere Kommentare zu hören bekommen.

Ich hoffe, man vergisst dabei nicht, auch das "Umfeld" zu berücksichtigen - wieso wurde diese Änderung nicht sofort als Fehler erkannt? Wurde die Konsequenz der Änderung nicht erkannt, oder wurde die Änderung gar nicht geprüft? Haben Prüfungen und/oder Tests versagt, oder liegt das Problem schon in der Organisation der Abläufe und es gibt keine Prüfungen und Tests?

Fragen über Fragen

Wie die Schwachstelle entstanden ist und ob es sich um einen Fehler oder eine absichtliche Backdoor handelt, kann nur Apple klären. Ich hoffe, es gibt irgendwann eine Erklärung und Apple kehrt die Ursache für dieses Desaster nicht unter den Teppich.

Interessant wäre es auch, zu wissen,

  • welche weiteren Änderungen es zwischen den beiden veröffentlichten Sourcecode-Versionen gibt. Vielleicht ist diese einzelne Zeile ja Bestandteil einer größeren Änderung und wurde beim Löschen einer if-Abfrage übersehen,
  • oder ob die Zeile vielleicht im Rahmen des Zusammenführens verschiedener Versionen entstanden ist. Wobei ein derartiger Merge-Fehler doch ziemliche Zweifel an der Tauglichkeit der verwendeten Merge-Tools aufwerfen würde.
  • seit wann Apple von der Schwachstelle weiß - die CVE-ID wurde am 8. Januar reserviert, aber das hat nichts zu bedeuten, die großen Softwarehersteller reservieren gerne mal größere Blöcke an IDs und verwenden die dann nach und nach.
  • wer die Schwachstelle entdeckt hat - Apple oder ein externer Forscher? Oder wurde sie womöglich bereits für Angriffe ausgenutzt?

Ich hoffe, Apple verrät uns möglichst bald, was da los war. Denn wo ein solcher Fehler entdeckt wurde, gibt es ja vielleicht noch weitere.

Carsten Eilers

Trackbacks

Dipl.-Inform. Carsten Eilers am : Neues zur iOS-Sicherheit, bösartigen E-Mails und Überwachungsmöglichkeiten

Vorschau anzeigen
Heute gibt es mal wieder eine bunte Mischung an Kommentaren. Los geht es mit einem Hinweis auf eine Diskussion im Blog von Bruce Schneier: Ist die "GOTO FAIL"-Lücke in iOS und Mac OS X Mavericks ein Fehler oder eine absichtliche Hintertü

Dipl.-Inform. Carsten Eilers am : 2014 - Das Jahr, in dem die Schwachstellen Namen bekamen

Vorschau anzeigen
2014 wird als das Jahr in die Geschichte eingehen, in dem die Schwachstellen Namen bekamen. Vorher gab es bereits Namen für Schadsoftware, aber für Schwachstellen haben die sich erst dieses Jahr wirklich durchgesetzt. Die Schwachstelle von

Dipl.-Inform. Carsten Eilers am : Drucksache: Windows Developer 10.15 - Wie sicher ist C# 6.0?

Vorschau anzeigen
Im windows.developer 10.15 ist ein Artikel zur Sicherheit von C# 6.0 erschienen. C# 6 ist da. Ebenso Visual Studio 2015 und .NET 4.6. Und auch wenn C#6 keine sicherheitsrelevanten Änderungen enthält gibt es Neuigkeiten rund um die S

entwickler.de am : PingBack

Die Anzeige des Inhaltes dieses Trackbacks ist leider nicht möglich.