Ausnutzung einer kritischen Spoofing-Schwachstelle in Windows CryptoAPI
Redaktion und weitere Beiträge von Tricia Howard
Zusammenfassung
Die Akamai Security Intelligence Group hat kürzlich eine kritische Schwachstelle in Windows CryptoAPI analysiert, die Microsoft von der National Security Agency (NSA) und dem National Cyber Security Center (NCSC) gemeldet wurde.
Die Schwachstelle, der CVE-2022-34689zugewiesen ist, hat einen CVSS-Wert von 7,5. Sie wurde im August 2022 gepatcht, doch erst im Oktober 2022 im Rahmen des Patch Tuesday öffentlich bekannt gegeben.
Laut Microsoft können sich Angreifer mit der Schwachstelle als legitime Entität tarnen.
Die Ursache des Fehlers ist die Annahme, dass der MD5-basierte Zertifikat-Cache-Indexschlüssel kollisionsfrei ist. Seit 2009 ist bekannt, dass die Kollisionsresistenz von MD5 nicht funktioniert.
Der Angriffsfluss umfasst zwei Phasen: In der ersten Phase muss ein legitimes Zertifikat geändert und dem Opfer zugestellt werden. Die zweite Phase umfasst die Erstellung eines neuen Zertifikats, dessen MD5 mit dem geänderten legitimen Zertifikat kollidiert, sowie die Verwendung des neuen Zertifikats, um die Identität des Subjekts des ursprünglichen Zertifikats zu fälschen.
Wir haben nach Anwendungen in freier Wildbahn gesucht, die CryptoAPI auf eine Art und Weise verwenden, die für diesen Spoofing-Angriff anfällig ist. Bisher haben wir festgestellt, dass alte Versionen von Chrome (v48 und niedriger) und Chromium-basierte Anwendungen ausgenutzt werden können. Doch wir glauben, dass es noch anfälligere Ziele gibt, weshalb unsere Forschung noch nicht abgeschlossen ist.
Wir haben festgestellt, dass weniger als 1 % der sichtbaren Geräte in Rechenzentren gepatcht werden. Das bedeutet, dass der Rest vor der Ausnutzung dieser Schwachstelle ungeschützt ist.
In diesem Blogbeitrag erläutern wir im Detail den potenziellen Angriffsablauf und die Folgen sowie einen Machbarkeitsnachweis (Proof of Concept, PoC), der den kompletten Angriff zeigt. Wir stellen auch eine OSQuery zur Erkennung anfälliger Versionen der CryptoAPI-Bibliothek bereit.
Hintergrund
Vor drei Monaten, in unserer Analyse des Patch Tuesday vom Oktober 2022, haben wir eine grundlegende Beschreibung einer kritischen Spoofing-Schwachstelle in Windows CryptoAPI veröffentlicht (CVE-2022-34689). Laut Microsoft können Angreifer mit dieser Schwachstelle „ihre Identität fälschen und Aktionen, wie Authentifizierung oder Codesignierung, als Zielzertifikat durchführen“.
CryptoAPI ist die API in Windows, die alle Aspekte rund um Kryptografie verarbeitet. Insbesondere werden hier Zertifikate verarbeitet – vom Lesen und Parsen bis hin zur Validierung anhand geprüfter Zertifizierungsstellen (Certification Authorities, CAs). Auch Browser verwenden CryptoAPI für die TLS-Zertifikatvalidierung – ein Prozess, durch den das Schlosssymbol in der Adressleiste angezeigt wird, auf das wir alle stets achten sollten.
Die Zertifikatüberprüfung ist jedoch nicht nur auf Browser beschränkt, sondern wird auch von anderen TLS-Clients verwendet, wie z. B. PowerShell-Webauthentifizierung, curl, wget, FTP-Manager, EDRs und viele weitere Anwendungen. Darüber hinaus werden Zertifikate zur Codesignierung in ausführbaren Dateien und Bibliotheken überprüft und Zertifikate zur Treibersignierung beim Laden von Treibern verifiziert. Wie man sich vorstellen kann, ist eine Schwachstelle bei der Überprüfung von Zertifikaten für Angreifer sehr lukrativ, da sie hiermit ihre Identität verschleiern und kritische Sicherheitsmaßnahmen umgehen können.
Das ist nicht das erste Mal, dass die National Security Agency eine Schwachstelle in CryptoAPI offenlegt. 2020 wurde CurveBall (CVE-2020-0601) gefunden und offengelegt. Die Ausnutzung von CurveBall oder CVE-2022-34689 führt zu Identitäts-Spoofing. Doch während von CurveBall viele Anwendungen betroffen sind, umfasst CVE-2022-34689 schwierigere Voraussetzungen und betrifft somit eine kleinere Gruppe anfälliger Ziele.
Schwachstellendetails
Um die Schwachstelle zu analysieren, haben wir zunächst versucht, den gepatchten Code zu finden. Wir haben BinDiff, ein beliebtes Tool zur Binärdifferenz, verwendet, um die verschiedenen Codeänderungen an CryptoAPI zu beobachten. In crypt32.dll wurde nur eine Funktion geändert: CreateChainContextFromPathGraph. Im Rahmen dieser Funktion werden zwei Zertifikate verglichen: eines, das als Eingabe empfangen wird, und ein anderes, das sich im Zertifikatcache der empfangenden Anwendung befindet (weitere Informationen zu diesem Cache folgen später).
Die Inspektion der Änderungen ergab, dass der Funktion an zwei Stellen memcmp-Prüfungen hinzugefügt wurden (Abbildung 1).
Vor dem Patch hat die Funktion nur anhand des MD5-Fingerabdrucks ermittelt, ob sich ein empfangenes Zertifikat bereits im Cache befindet (und daher bereits verifiziert wurde). Nach dem Patch erfordert der memcmp-Zusatz, dass der tatsächliche Inhalt der beiden Zertifikate vollständig übereinstimmt.
An diesem Punkt haben wir angenommen, dass ein Angreifer, der ein schädliches Zertifikat bereitstellen kann – dessen MD5 mit einem Zertifikat kollidiert, das sich bereits im Zertifikatcache des Opfers befindet – die Schwachstellenprüfung umgehen und sein schädliches Zertifikat als vertrauenswürdig einstufen kann (Abbildung 2).
Zertifikatcache von CryptoAPI
CryptoAPI kann einen Cache für empfangene Endzertifikate verwenden, um Performance und Effizienz zu verbessern. Dieser Mechanismus ist standardmäßig deaktiviert. Um ihn zu aktivieren, muss der Anwendungsentwickler bestimmte Parameter an CertGetCertificateChainübergeben. Das ist die Windows-API-Funktion, die letztendlich zu dem anfälligen Code führt (Abbildung 3).
CertGetCertificateChain empfängt mehrere interessante Parameter:
hChainEngine – ein konfigurierbares Objekt, das verwendet wird, um die Art und Weise zu steuern, wie Zertifikate validiert werden
pCertContext – der Kontext des Eingangszertifikats – eine Datenstruktur, die auf Grundlage des Eingangszertifikats durch die WinAPI-Funktion „CertCreateCertificateContext“ erstellt wird
dwFlags – die Flags, die weitere Konfigurationen angeben
ppChainContext – das Ausgabeobjekt, das (neben anderen Feldern) den Vertrauensstatus enthält, d. h. das Überprüfungsurteil der Kette
Um das Caching-Verfahren für Endzertifikate zu aktivieren, muss der Entwickler entweder das Flag CERT_CHAIN_CACHE_END_CERT in dwFlagssetzen oder eine nutzerdefinierte Zertifikatsketten-Engine erstellen und das Flag CERT_CHAIN_CACHE_END_CERT im Feld dwFlags setzen.
Um zu verstehen, wie der Cache implementiert und verwendet wird, werfen wir einen Blick auf die Funktion FindIssuerObject, die das Zertifikat aus dem Cache abruft. Im Großen und Ganzen verhält sich die Funktion wie folgt:
Sie berechnet den Bucket-Index des Eingangszertifikats im Cache anhand der vier am wenigsten signifikanten Bytes seines MD5-Fingerabdrucks.
Wenn es im Cache vorhanden ist, vergleicht die Funktion den gesamten MD5-Fingerabdruck des zwischengespeicherten Zertifikats mit dem Eingangszertifikat.
Wenn die Fingerabdrücke übereinstimmen (Cachetreffer), wird das Eingangszertifikat als vertrauenswürdig eingestuft und zurückgegeben. Von nun an verwendet die Anwendung die Attribute des Eingangszertifikats (wie den öffentlichen Schlüssel, den Aussteller usw.) und nicht das zwischengespeicherte Zertifikat.
Wenn die Fingerabdrücke nicht übereinstimmen (Cachefehler), geht die Funktion zum nächsten Zertifikat im Bucket, vergleicht den MD5-Fingerabdruck und wiederholt den Vorgang.
Microsoft vertraut der Gültigkeit zwischengespeicherter Zertifikate und führt keine weiteren Gültigkeitsprüfungen durch, nachdem ein Endzertifikat im Cache gefunden wurde. Das ist an sich eine vernünftige Annahme. Doch der Code geht auch davon aus, dass zwei Zertifikate identisch sind, wenn ihre MD5-Fingerabdrücke übereinstimmen. Das ist eine falsche Annahme, die ausgenutzt werden kann – und sie war auch der Grund für den Patch.
Um unsere Hypothese zu untermauern, haben wir eine kleine Anwendung geschrieben, die CertGetCertificateChain verwendet, und den Ablauf der Zertifikatüberprüfung in crypt32.dll debuggt. Mit WinDbg haben wir ein Szenario simuliert, in dem der MD5-Fingerabdruck unseres eigenen (selbstsignierten) Zertifikats mit einem gültigen Zertifikat übereinstimmt, das sich bereits im Cache befand. Wie in Abbildung 4 gezeigt, wurde unser eigens erstelltes Zertifikat als vertrauenswürdig eingestuft.
Indem wir nur eine Überprüfung umgingen, konnten wir Windows glauben machen, dass unser Schadzertifikat legitim war.
So kann die Schwachstelle ausgenutzt werden
Das Erstellen eines Zertifikats mit einem MD5-Fingerabdruck, der genau mit einem bestimmten MD5-Wert übereinstimmt, wird als „Urbild-Angriff“ bezeichnet – doch das ist selbst mit der heutigen Rechenleistung nicht machbar. Es ist jedoch möglich, auf effiziente Weise zwei Zertifikate mit zwei ausgewählten Präfixen zu generieren, die am Ende die gleichen MD5-Fingerabdrücke aufweisen. Diese Art von Angriff wird als „Chosen Prefix Collision“ („Kollisionsangriff mit gewähltem Präfix“ oder kurz: „Präfix-Kollisionsangriff“) bezeichnet.
Wenn wir diesen Weg wählen, müssen wir der Anwendung des Opfers irgendwie zwei Zertifikate vorlegen. Ein Zertifikat wird korrekt signiert, verifiziert und zwischengespeichert (wir bezeichnen es als „modifiziertes Zielzertifikat“). Er wird so generiert, dass ein Präfix-Kollisionsangriff möglich ist. Das zweite Zertifikat (das wir „schädliches Zertifikat“ nennen) enthält die gefälschte Identität. Es kollidiert mit dem MD5-Fingerabdruck des ersten Zertifikats (Abbildung 5).
Zertifikat-Spoofing über MD5-Kollisionen
MD5-Kollisionen führen uns etwa 14 Jahre zurück – in eine Zeit, als Beyoncé „Single Ladies“ veröffentlichte, Obama zum ersten Mal zum US-Präsidenten gewählt wurde und MD5-Kollisionen zum ersten Mal verwendet wurden, um SSL-Zertifikate zu fälschen. Es gibt einen großen Unterschied zwischen diesem ersten Angriff und dem Szenario, mit dem wir uns heute befassen: Das vorherige Szenario griff MD5-Signaturen, während wir es bei der aktuellen Schwachstelle mit MD5-Fingerabdrückenzu tun haben. Sehen wir uns den Unterschied genauer an:
Laut RFC 5280, Abschnitt 4.1, ist ein Zertifikat eine ASN.1-Sequenz mit zwei Abschnitten (Abbildung 6):
tbsCertificate (oder „to-be-signed Certificate“, also zu unterzeichnendes Zertifikat): Dies ist der Teil, der alle identitätsbezogenen Details enthält (Betreff, öffentlicher Schlüssel, Seriennummer, EKU usw.). Das ist der Teil, der signiert ist.
signatureAlgorithm und signatureValue – diese Felder umfassen die Signatur des TBS.
Certificate ::= SEQUENCE {
tbsCertificate TBSCertificate,
signatureAlgorithm AlgorithmIdentifier,
signatureValue BIT STRING }
Abb. 6: Die ASN.1-Sequenz, die Zertifikate definiert
Eine Zertifikat-Signatur ist dementsprechend eine Struktur, die in das Zertifikat eingebettet ist und nur den TBS-Teil des Zertifikats signiert. Auf der anderen Seite ist der Fingerabdruck des Zertifikats ein Hash des gesamten Zertifikats (einschließlich Signatur).
Wenn wir also einen Teil des Zertifikats ändern könnten, der außerhalb des TBS liegt, ohne das Zertifikat für ungültig zu erklären, dann würden wir den Fingerabdruck ändern, ohne die Signatur zu ändern. Wenn der Parser die Signatur korrekt analysiert und der TBS unverändert bleibt, wird das Zertifikat trotzdem als gültig und signiert betrachtet, obwohl sich die vollständige Zertifikatstruktur geändert hat (Abbildung 7).
MD5-Präfix-Kollisionsangriff – eine kurze Übersicht
Angenommen, Sie haben zwei beliebige Zeichenfolgen, A und B, mit der gleichen Länge. Dann können zwei Zeichenfolgen, C und D, wie folgt effizient berechnet werden:
MD5(A || C) = MD5(B || D) |
|| gibt hierbei eine Zeichenfolgenverkettung an.
Darüber hinaus wird nicht nur das endgültige MD5-Ergebnis gleich sein, sondern auch der interne MD5-Status nach dem Anhängen von C oder D. Mit einem Suffix E erhalten wir also:
MD5(A || C || E) = MD5(B || D || E) |
(Vorausgesetzt, das gleiche Suffix E wird auf beiden Seiten hinzugefügt).
Raum für Kollisionsblöcke
Als Angreifer müssen wir ein Zertifikat generieren, das gültig erscheint, aber auch Raum für Kollisionsblöcke enthält (die Zeichenfolgen C und D in der obigen Erläuterung). Auf diese Weise können wir unser Schadzertifikat (mit demselben MD5-Fingerabdruck) erstellen, das wir als Nächstes bereitstellen.
Laut RFC 5280, Abschnitt 4.1.1.2, lautet die Struktur von signatureAlgorithm wie folgt:
AlgorithmIdentifier ::= SEQUENCE {
algorithm OBJECT IDENTIFIER,
parameters ANY DEFINED BY algorithm OPTIONAL }
Das Parameterfeld für den RSA-Algorithmus (basierend auf RFC 3279) „SOLL der ASN.1-Typ NULL sein“. Mit anderen Worten: RSA verwendet keine Signaturparameter, sondern verwendet NULL als Wert. Ist es möglich, dass CryptoAPI dieses Feld für RSA-Signaturen ignoriert?
Um Platzhalterbytes in dieses Feld einzufügen (als Vorbereitung für Kollisionsblöcke), wurde versucht, den ASN.1-Typ von NULL zu BIT STRING zu ändern. Bei Tests mit CryptoAPI und OpenSSL funktioniert diese Methode – das Zertifikat gilt weiterhin als gültig. Die Signatur ist unverändert und weiterhin gültig, da wir den TBS nicht geändert haben. (Doch natürlich ändert sich der MD5-Fingerabdruck.)
Kollisionen beim Zertifikat-MD5-Fingerabdruck
Jetzt können wir die Informationen kombinieren und ein Rezept für die Manipulation eines vorhandenen, bereits signierten Zertifikats bereitstellen, sodass es mit dem MD5-Fingerabdruck eines schädlichen Zertifikats kollidieren kann.
Wir nehmen ein legitimes RSA-signiertes Endzertifikat, z. B. das TLS-Zertifikat einer Website (unser „Zielzertifikat“).
Wir ändern alle interessanten Felder (Betreff, Erweiterungen, EKU, öffentlicher Schlüssel usw.) im TBS-Teil des Zertifikats, um das schädliche Zertifikat zu erstellen. Hinweis: Wir ändern die Signatur nicht, sodass das schädliche Zertifikat falsch signiert ist. Das Ändern des öffentlichen Schlüssels ist hier wichtig – dadurch kann der Angreifer das schädliche Zertifikat signieren.
Wir ändern das Feld Parameter des Feldes signatureAlgorithm beider Zertifikate, sodass genügend Raum für MD5-Kollisionsblöcke (C und D in der obigen Erläuterung) vorhanden ist, die mit dem gleichen Offset für beide Zertifikate beginnen.
Wir schneiden beide Zertifikate an der Stelle ab, an der MD5-Kollisionsblöcke platziert werden sollen.
Wir führen eine Berechnung der MD5-Präfixkollision durch und kopieren das Ergebnis in die Zertifikate.
Wir verketten den Signaturwert des legitimen Zertifikats (Suffix E in der obigen Erläuterung) mit beiden unvollständigen Zertifikaten.
Ein Beispiel aus der Praxis
Mit unserem Verständnis von MD5-Kollisionen können wir jetzt versuchen, diese CVE bei einem realen Ziel auszunutzen. Unter den zahlreichen Anwendungen, die wir überprüft haben, konnten wir ein gefährdetes Ziel finden: Chrome v48. (Diese Anwendung ist anfällig, weil sie das Flag CERT_CHAIN_CACHE_END_CERT an CertGetCertificateChain übergibt.) Andere Chromium-basierte Anwendungen aus dieser Zeit sind ebenfalls anfällig für diese CVE.
Um diesen Exploit ausnutzen zu können, mussten wir zunächst zwei Zertifikate erstellen, die den gleichen MD5-Fingerabdruck aufweisen. Das haben wir mit HashClash erreicht (Abbildung 8).
Dann mussten wir einen Weg finden, unser geändertes Zielzertifikat in den Chrome-Cache zu injizieren. Das war schwierig, da es unmöglich ist, ein Zertifikat bereitzustellen, ohne seinen privaten Schlüssel zu kennen.
In TLS 1.2 gibt es zwei relevante Verifizierungsstufen:
Die Meldung Server-Schlüsselaustausch – diese Nachricht kann nur von jemandem erstellt werden, der den privaten Schlüssel des Zertifikats kennt, da sie vom Zertifikat signiert wird.
Die Meldung Server-Handshake beendet – diese Meldung überprüft alle vorherigen Handshake-Nachrichten auf Manipulationsschutz
(TLS 1.3 ist anders; hierauf haben wir uns nicht konzentriert).
Denken Sie daran, dass wir in der ersten Phase des Angriffs das geänderte Zertifikat in den Endzertifikat-Cache Chrome injizieren möchten.
Mithilfe eines Python-Skripts als Proxy führen wir einen MITM-Angriff (Machine in the Middle) durch:
Unser schädlicher MITM-Server kommuniziert mit dem echten Server und spiegelt die ersten Meldungen des TLS-Handshakes an das Opfer.
In der Serverzertifikat-Meldung ändert unser schädlicher MITM-Server die Meldung des realen Servers und ersetzt das reale Zielzertifikat durch das geänderte Zertifikat.
Die Meldung „Server-Schlüsselaustausch“ kann ohne Änderungen gespiegelt werden.
Unser schädlicher Server kann die Meldung „Server Handshake beendet“ nicht einfach weiterleiten, da der Handshake tatsächlich manipuliert wurde. Daher wird die Verbindung beendet.
Um die Meldung „Server-Schlüsselaustausch“ zu überprüfen, muss Chrome das geänderte Zertifikat mit CryptoAPI laden – und so würde sie in den Cache injiziert. Chrome behandelt die unterbrochene Verbindung nicht als TLS-Sicherheitsproblem – es könnte einfach ein zufälliges Netzwerkproblem sein. Chrome versucht, eine Verbindung herzustellen, und dieses Mal sendet der schädliche Server eine Website mit dem schädlichen Zertifikat, anstatt Nachrichten von der echten Website zu spiegeln. Chrome überspringt den vollständigen Verifizierungsprozess, da der Browser davon ausgeht, dass sich das Zertifikat bereits im Cache befindet. Das Ergebnis ist ein nahtloser Besuch einer scheinbar legitimen Microsoft-Website (Abbildungen 9 und 10). Den vollständigen Exploit-Ablauf sehen Sie in unserem Video.
Erkennung
Wir stellen eine OSQuery bereit, um anfällige Versionen von crypt32.dll, der anfälligen Bibliothek, zu erkennen (Abbildung 11). Kunden von Akamai Guardicore Segmentation können die Insight-Funktion zusammen mit dieser Abfrage verwenden, um nach anfälligen Assets zu suchen.
Bedenken Sie, dass ein Asset nur anfällig ist, wenn es eine nicht gepatchte Version von crypt32.dll verwendet und eine anfällige Anwendung ausführt. (Bis heute haben wir die Anfälligkeit nur bei Chrome v48 festgestellt.)
WITH product_version AS (
WITH os_minor AS (
WITH os_major AS (
SELECT substr(product_version, 0, instr(product_version, ".")) as os_major, substr(product_version, instr(product_version, ".")+1) as no_os_major_substr
FROM file
WHERE path = "c:\windows\system32\crypt32.dll"
)
SELECT substr(no_os_major_substr, instr(no_os_major_substr, ".")+1) as no_os_minor_substr, substr(no_os_major_substr, 0, instr(no_os_major_substr, ".")) as os_minor, os_major
FROM os_major
)
SELECT
CAST(substr(no_os_minor_substr, instr(no_os_minor_substr, ".")+1) AS INTEGER) AS product_minor,
CAST(substr(no_os_minor_substr, 0, instr(no_os_minor_substr, ".")) AS INTEGER) AS product_major,
CAST(os_minor AS INTEGER) AS os_minor,
CAST(os_major AS INTEGER) AS os_major
FROM os_minor
)
SELECT
CASE
WHEN os_major = 6 AND os_minor = 3 THEN "not supported"
WHEN (
(product_major = 20348 AND product_minor >= 887)
OR
(product_major = 17763 AND product_minor >= 3287)
OR
(product_major = 14393 AND product_minor >= 5291)
OR
(product_major >= 19041 AND product_minor >= 1889)
)
THEN
"patched"
ELSE
"not patched"
END is_patched
FROM product_version
Fazit
Zertifikate spielen eine wichtige Rolle bei der Online-Identitätsüberprüfung, wodurch diese Schwachstelle für Angreifer lukrativ ist. Obwohl die Schwachstelle als kritisch eingestuft war, erhielt sie nur einen CVSS-Wert von 7,5. Wir glauben, dass dies auf den begrenzten Umfang anfälliger Anwendungen und Windows-Komponenten zurückzuführen ist, bei denen die Voraussetzungen für die Schwachstelle erfüllt sind.
Dennoch gibt es immer noch viel Code, der diese API verwendet und dieser Schwachstelle ausgesetzt sein könnte, sodass selbst für nicht mehr angebotene Versionen von Windows, wie Windows 7, ein Patch erforderlich ist.
Wir empfehlen Ihnen, Ihre Windows-Server und -Endpoints mit dem neuesten Sicherheitspatch von Microsoft zu patchen. Um diese Schwachstelle zu beheben, können Entwickler auch andere WinAPIs verwenden, um die Gültigkeit eines Zertifikats zu überprüfen, bevor es verwendet wird, z. B. CertVerifyCertificateChainPolicy. Beachten Sie, dass Anwendungen, die kein Endzertifikat-Caching verwenden, nicht anfällig sind.
Unseren PoC-Code finden Sie in unserem GitHub-Repository. Sie können sich auch über unser Twitter-Konto über alle Veröffentlichungen der Akamai Security Intelligence Group auf dem Laufenden halten.