
Analyse d'une vulnérabilité logique dans le client SSH macOS entraînant l'exposition de la phrase de passe du client à un attaquant local
Ce document présente à la fois une analyse d’une vulnérabilité logique identifiée dans le binaire ssh sous macOS (ma première vulnérabilité logicielle !) et une analyse du correctif montrant comment Apple a corrigé la vulnérabilité. J’ai signalé le problème à Apple via le programme Apple Security Bounty fin 2022, qui a ensuite été corrigé dans macOS Ventura 13.5 et a donné lieu à CVE-2023-42829 (🎉). Le problème conduit à ce que les phrases de passe SSH enregistrées dans le trousseau « Connexion » local de l’utilisateur macOS (dans le groupe d’accès com.apple.ssh.passphrases) soient exposées en texte clair à un attaquant local.
Avertissement :
Ce rapport est fourni à des fins éducatives uniquement, les processus de divulgation responsable ayant été suivis. L’analyse est fournie en l’état, et toute publication ou utilisation ultérieure de ces informations doit respecter les directives de divulgation responsable.
Flux haut niveau corrigé
Flux haut niveau non corrigé
com.apple.private.security.clear-library-validation| Matériel | Logiciel OS |
|---|---|
| MacBook Pro M1 2021 | /usr/bin/ssh sous macOS Ventura build 13.0.1 (22A400) |
La preuve de concept suivante illustre la simplicité relative d’exploitation de la vulnérabilité, en passant une bibliothèque dynamique au paramètre -I du binaire ssh :
jamesd@local build % ssh -I /Users/jamesd/rome.dylib [email protected]
SSH Private Key Passphrase -> 'SuperSecret!!@@$'
Parlons de la découverte et des raisons de ce problème !
Il était une fois où j’utilisais le binaire ssh pour une action anodine et j’ai fait une faute de frappe en utilisant le paramètre -i (utilisé pour passer un fichier d’identité SSH) avec le paramètre -I et j’ai obtenu la sortie standard suivante :
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), ...
...
Après avoir vérifié les entitlements du binaire ssh...
jamesd@local build % ldid -e /usr/bin/ssh # vider les entitlements du binaire /usr/bin/ssh
<key>com.apple.private.security.clear-library-validation</key>
<true/>
<key>keychain-access-groups</key>
<array>
<string>com.apple.ssh.passphrases</string>
</array>
...mon intérêt a été piqué : le binaire possède des entitlements pour lire un groupe d’accès protégé du trousseau (com.apple.ssh.passphrases), a l’entitlement com.apple.private.security.clear-library-validation, et ssh tente d’appeler dlopen() sur ma clé privée ?
Il s’avère que ssh prend en charge l’authentification à un système distant via un mécanisme appelé pkcs11, une norme pour les opérations cryptographiques sur les modules de sécurité matériels (HSM) (https://docs.aws.amazon.com/cloudhsm/latest/userguide/pkcs11-library.html).
Nous n’avons pas besoin de nous attarder sur les détails de pkcs11 pour ce compte rendu, si ce n’est que le client fournit une bibliothèque pkcs11 (bibliothèque dynamique) à ssh -I.
com.apple.private.security.clear-library-validationBien que conceptuellement équivalent, com.apple.private.security.clear-library-validation diffère de l’entitlement équivalent précédent (com.apple.security.cs.disable-library-validation) car com.apple.private.security.clear-library-validation nécessite d’appeler l’appel système csops() en passant CS_OPS_CLEAR_LV pour contrôler l’activation/désactivation de la validation de bibliothèque, afin de mieux contrôler l’intégrité du processus à l’exécution (contrairement à com.apple.private.security.clear-library-validation qui permettrait probablement de charger n’importe quelle bibliothèque sans contrôle à l’exécution). (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/)
Comme csops(CS_OPS_CLEAR_LV) est appelé avant dlopen() de notre bibliothèque, celle-ci se charge simplement et le constructeur s’exécute, ce qui nous permet de fournir une bibliothèque dynamique malveillante se faisant passer pour une bibliothèque pkcs11 et d’exécuter du code dans le contexte de /usr/bin/ssh et d’utiliser l’entitlement keychain-access-groups.
Peut-être que le correctif impliquait des vérifications effectuées sur le binaire avant d’appeler csops() pour s’assurer qu’il possède une identité de signature de confiance spécifique ?
Non !
Après la publication du correctif (22G74), j’ai comparé l’implémentation non sécurisée de pkcs11_add_provider() (la méthode dans laquelle l’appel à dlopen() est effectué) et elle semblait identique à la version corrigée – mais il manque un entitlement dans /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>
Mais Apple n’a sûrement pas supprimé la prise en charge de pkcs11 de SSH ? J’ai essayé de ré-exploiter la vulnérabilité à l’aide de mon PoC et j’ai constaté que, bien que ma bibliothèque soit chargée, elle ne pouvait plus lire le groupe d’accès du trousseau et semblait s’exécuter dans le contexte de /usr/libexec/ssh-apple-pkcs11 (un binaire que je n’avais jamais vu auparavant) qui possède les entitlements suivants :
<?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>