
Analyse einer Logik-Schwachstelle im macOS SSH Client, die zur Offenlegung der Client-Passphrase gegenüber einem lokalen Angreifer führt.
Dieses Dokument enthält sowohl eine Analyse einer Logik-Schwachstelle, die im ssh-Binary auf macOS identifiziert wurde (meine erste Software-Sicherheitslücke!), als auch eine Patch-Analyse, die zeigt, wie Apple die Schwachstelle behoben hat. Ich meldete das Problem Apple über das Apple Security Bounty Programm Ende 2022, das später in macOS Ventura 13.5 behoben wurde und CVE-2023-42829 (🎉) ergab. Das Problem führt dazu, dass SSH-Passphrasen, die in der benutzerlokalen 'Login'-Keychain von macOS (in der Zugriffsgruppe com.apple.ssh.passphrases) gespeichert sind, einem lokalen Angreifer im Klartext offengelegt werden.
Haftungsausschluss:
Dieser Bericht dient ausschließlich zu Bildungszwecken, wobei verantwortungsvolle Offenlegungsprozesse eingehalten wurden. Die Analyse wird wie besehen angeboten, und jede weitere Veröffentlichung oder Nutzung dieser Informationen sollte sich an die Richtlinien für verantwortungsvolle Offenlegung halten.
Gepatchter High-Level-Ablauf
Nicht gepatchter High-Level-Ablauf
com.apple.private.security.clear-library-validation Berechtigung| Hardware | Betriebssystem-Software |
|---|---|
| MacBook Pro M1 2021 | /usr/bin/ssh @ macOS Ventura build 13.0.1 (22A400) |
Das folgende Proof-of-Concept veranschaulicht die relativ einfache Ausnutzbarkeit der Schwachstelle, indem eine dynamische Bibliothek an das -I-Flag des ssh-Binaries übergeben wird:
jamesd@local build % ssh -I /Users/jamesd/rome.dylib [email protected]
SSH-Private-Key-Passphrase -> 'SuperSecret!!@@$'
Lassen Sie uns darüber sprechen, wie dies entdeckt wurde und warum es passierte!
Es war einmal, als ich das ssh-Binary für etwas Harmloses verwendete und mich beim -i-Flag (zum Übergeben einer SSH-Identitätsdatei) mit dem -I-Flag vertippte und auf folgende Standardausgabe stieß:
jamesd@local build % ssh -I /Users/jamesd/.ssh/id_rsa something@somewhere
dlopen /Users/jamesd/.ssh/id_rsa failed: dlopen(/Users/jamesd/.ssh/id_rsa, 0x0002): tried: '/Users/jamesd/.ssh/id_rsa' (not a mach-o file), ...
...
Nachdem ich die Berechtigungen des ssh-Binaries überprüft hatte...
jamesd@local build % ldid -e /usr/bin/ssh # dump the entitlements of the /usr/bin/ssh binary
<key>com.apple.private.security.clear-library-validation</key>
<true/>
<key>keychain-access-groups</key>
<array>
<string>com.apple.ssh.passphrases</string>
</array>
...mein Interesse war geweckt – das Binary hat Berechtigungen, aus einer geschützten Keychain-Zugriffsgruppe (com.apple.ssh.passphrases) zu lesen, besitzt die com.apple.private.security.clear-library-validation-Berechtigung, und ssh versucht, meinen privaten Schlüssel zu dlopen()?
Wie sich herausstellt, unterstützt ssh die Authentifizierung gegenüber einem entfernten System über etwas namens pkcs11, einem Standard für kryptografische Operationen auf Hardware-Sicherheitsmodulen (HSMs) (https://docs.aws.amazon.com/cloudhsm/latest/userguide/pkcs11-library.html).
Für die Zwecke dieses Berichts müssen wir uns nicht um die Einzelheiten von pkcs11 kümmern, abgesehen davon, dass der Client eine pkcs11-Bibliothek (dynamische Bibliothek) an ssh -I übergibt.
com.apple.private.security.clear-library-validation BerechtigungObwohl konzeptionell gleichwertig, unterscheidet sich com.apple.private.security.clear-library-validation von der vorherigen gleichwertigen Berechtigung (com.apple.security.cs.disable-library-validation), da com.apple.private.security.clear-library-validation erfordert, dass der csops()-Systemaufruf mit CS_OPS_CLEAR_LV aufgerufen wird, um die Bibliotheksvalidierung zu aktivieren/deaktivieren und so eine größere Kontrolle über die Prozessintegrität zur Laufzeit zu behalten (im Vergleich zu com.apple.private.security.clear-library-validation, das vermutlich das Laden jeder Bibliothek ohne Laufzeitkontrolle erlauben würde). (https://github.com/apple-oss-distributions/xnu/blob/main/bsd/kern/kern_proc.c) (https://theevilbit.github.io/posts/com.apple.private.security.clear-library-validation/)
Da csops(CS_OPS_CLEAR_LV) vor dem dlopen() unserer Bibliothek aufgerufen wird, wird unsere Bibliothek einfach geladen und der Konstruktor ausgeführt, sodass wir eine bösartige dynamische Bibliothek, die als pkcs11-Bibliothek getarnt ist, bereitstellen und Code im Kontext von /usr/bin/ssh ausführen und die keychain-access-groups-Berechtigung nutzen können.
Vielleicht beinhaltete der Patch Überprüfungen des Binaries vor dem Aufruf von csops(), um nach bestimmten vertrauenswürdigen Signieridentitäten zu suchen?
Nein!
Nach der Veröffentlichung des Patches (22G74) verglich ich die unsichere Implementierung von pkcs11_add_provider() (die Methode, in der der Aufruf von dlopen() erfolgt) und sie schien mit der Patch-Version identisch zu sein – aber es fehlt eine Berechtigung in /usr/bin/ssh?
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>keychain-access-groups</key>
<array>
<string>com.apple.ssh.passphrases</string>
</array>
</dict>
</plist>
Aber Apple hat doch sicherlich nicht die pkcs11-Unterstützung aus SSH entfernt? Ich versuchte, die Schwachstelle mit meinem PoC erneut auszunutzen und stellte fest, dass meine Bibliothek zwar geladen wurde, aber nicht mehr aus der Keychain-Zugriffsgruppe lesen konnte und offenbar im Kontext von /usr/libexec/ssh-apple-pkcs11 (ein mir unbekanntes Binary) ausgeführt wurde, das die folgenden Berechtigungen besitzt:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.private.security.clear-library-validation</key>
<true/>
</dict>
</plist>
Ein Kommentar im csops()-Systemaufruf für CS_OPS_CLEAR_LV (https://github.com/apple-oss-distributions/xnu/blob/main/bsd/kern/kern_proc.c) erwähnt das erneute Ausführen (re-exec) in ein Binary ohne Bibliotheksvalidierung als Alternative zu CS_OPS_CLEAR_LV, anstatt es in Kombination zu verwenden. Warum ist die logisch fehlerhafte Routine immer noch in pkcs11_add_provider() in /usr/bin/ssh vorhanden, wenn es jetzt ein zusätzliches Hilfsbinary gibt? Und warum erscheint die Implementierung im Patch unverändert?
Nun, wie sich herausstellte, fügte der Patch kein Hilfsbinary hinzu, sondern verteilte zwei ssh-Binaries in macOS: /usr/bin/ssh und /usr/libexec/ssh-apple-pkcs11 – beide sind bis auf ihre Berechtigungen identisch (ich habe Diaphora verwendet, um dies zu validieren):
Nach einer dynamischen Analyse des gepatchten ssh-Binaries wurde eine zusätzliche Überprüfung in der (ziemlich großen) start()-Routine von ssh hinzugefügt, die dazu dient, bei Verwendung von pkcs11-Funktionen zu ermitteln, ob das Binary im Kontext von /usr/bin/ssh oder /usr/libexec/ssh-apple-pkcs11 ausgeführt wird:
Im grünen Block sehen wir einen Aufruf von SecTaskCopyValueForEntitlement() (der übergebene Wert ist "com.apple.private.security.clear-library-validation"), der dann (im orangenen Block) ausgewertet wird und einen bedingten Aufruf der erneuten Ausführungsroutine verursacht, wenn die "com.apple.private.security.clear-library-validation"-Berechtigung für die aktuelle Aufgabe/den aktuellen Prozess nicht vorhanden ist (roter Block).
Dies führt dazu, dass das weniger berechtigte Binary ssh-apple-pkcs11 verwendet wird, wenn die vom Benutzer bereitgestellte pkcs11-Bibliothek geladen wird (wodurch die keychain-bezogenen Funktionen von ssh deaktiviert werden), und ssh dort verwendet wird, wo keine nicht vertrauenswürdigen Bibliotheken geladen werden sollen (wodurch die keychain-bezogenen Funktionen von ssh aktiviert werden).
Wir können dies auch durch das Debuggen des gepatchten validieren
Wenn wir das gepatchte ssh-Binary (ausgeliefert in macOS 22G74) debuggen und Haltepunkte auf execv() setzen, können wir tatsächlich einen Aufruf von execv() erkennen, wenn das -I-Flag übergeben wird, der bei einem Start von ssh ohne Verwendung von pkcs11-Funktionen nicht auftritt:
Ich meldete das Problem Apple über das Apple Security Bounty Programm Ende 2022 und es wurde später in macOS Ventura 13.5 behoben und ergab CVE-2023-42829 (🎉).
Dieser Bericht ist eine unabhängige Veröffentlichung und wurde nicht von Apple Inc. autorisiert, gesponsert oder anderweitig genehmigt. macOS, iOS und iWork sind Marken von Apple Inc.