
Análisis de una vulnerabilidad lógica en el cliente SSH de macOS que provoca la exposición de la frase de contraseña del cliente a un atacante local.
Este documento presenta tanto un análisis de una vulnerabilidad lógica identificada en el binario ssh de macOS (¡mi primera vulnerabilidad de software!), como un análisis del parche que demuestra cómo Apple corrigió la vulnerabilidad. Reporté el problema a Apple a través del programa Apple Security Bounty a finales de 2022, que luego se corrigió en macOS Ventura 13.5 y dio lugar a CVE-2023-42829 (🎉). El problema provoca que las frases de contraseña SSH guardadas en el llavero 'Login' local del usuario de macOS (en el grupo de acceso com.apple.ssh.passphrases) queden expuestas en texto plano a un atacante local.
Descargo de responsabilidad:
Este informe se proporciona únicamente con fines educativos, habiéndose seguido procesos de divulgación responsable. El análisis se ofrece tal cual, y cualquier publicación o uso posterior de esta información debe ajustarse a las pautas de divulgación responsable.
Flujo de alto nivel con parche
Flujo de alto nivel sin parche
com.apple.private.security.clear-library-validation Entitlement| Hardware | Software del SO |
|---|---|
| MacBook Pro M1 2021 | /usr/bin/ssh @ macOS Ventura build 13.0.1 (22A400) |
La siguiente prueba de concepto ilustra la facilidad de explotación de la vulnerabilidad, pasando una biblioteca dinámica al indicador -I del binario ssh:
jamesd@local build % ssh -I /Users/jamesd/rome.dylib [email protected]
SSH Private Key Passphrase -> 'SuperSecret!!@@$'
¡Hablemos de cómo se descubrió esto y por qué sucedió!
Una vez estaba usando el binario ssh para algo inofensivo y confundí el indicador -i (usado para pasar un archivo de identidad SSH) con el indicador -I, y me encontré con la siguiente salida estándar:
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), ...
...
Después de comprobar los entitlements del binario ssh...
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>
...mi interés se despertó: el binario tiene entitlements para leer de un grupo de acceso protegido del llavero (com.apple.ssh.passphrases), posee el entitlement com.apple.private.security.clear-library-validation, y ssh está intentando hacer dlopen() de mi clave privada?
Resulta que ssh admite la autenticación en un sistema remoto mediante algo llamado pkcs11, un estándar para operaciones criptográficas en módulos de seguridad de hardware (HSM) (https://docs.aws.amazon.com/cloudhsm/latest/userguide/pkcs11-library.html).
No necesitamos preocuparnos por los detalles específicos de pkcs11 para los fines de este artículo, aparte del hecho de que el cliente suministra una biblioteca pkcs11 (biblioteca dinámica) a ssh -I.
com.apple.private.security.clear-library-validation EntitlementAunque conceptualmente equivalentes, com.apple.private.security.clear-library-validation difiere del entitlement equivalente anterior (com.apple.security.cs.disable-library-validation) ya que com.apple.private.security.clear-library-validation requiere que se realice la llamada al sistema csops() pasando CS_OPS_CLEAR_LV para controlar la habilitación/deshabilitación de la validación de bibliotecas y mantener un mayor control sobre la integridad del proceso en tiempo de ejecución (en comparación con com.apple.private.security.clear-library-validation, que presumiblemente permitiría cargar cualquier biblioteca sin control en tiempo de ejecución). (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/)
Dado que csops(CS_OPS_CLEAR_LV) se llama antes de hacer dlopen() de nuestra biblioteca, nuestra biblioteca simplemente se carga y el constructor se ejecuta, permitiéndonos suministrar una biblioteca dinámica maliciosa que se hace pasar por una biblioteca pkcs11 y ejecutar código desde el contexto de /usr/bin/ssh y utilizar el entitlement keychain-access-groups.
Quizás el parche implicaba comprobaciones realizadas sobre el binario antes de llamar a csops() para verificar identidades de firma específicas de confianza?
¡No!
Tras el lanzamiento del parche (22G74), comparé la implementación insegura de pkcs11_add_provider() (el método en el que se realiza la llamada a dlopen()) y parecía idéntica a la de la versión del parche, pero falta un entitlement en /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>
Pero seguramente Apple no ha eliminado la compatibilidad con pkcs11 de SSH? Intenté re-explotar la vulnerabilidad usando mi PoC y descubrí que, aunque mi biblioteca se cargaba, ya no podía leer del Grupo de Acceso del Llavero y parecía ejecutarse en el contexto de /usr/libexec/ssh-apple-pkcs11 (un binario que no había visto antes), que posee los siguientes entitlements:
<?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>