
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>
Un comentario en la llamada al sistema csops() para CS_OPS_CLEAR_LV (https://github.com/apple-oss-distributions/xnu/blob/main/bsd/kern/kern_proc.c) menciona la re-ejecución en un binario sin validación de bibliotecas como alternativa a CS_OPS_CLEAR_LV, en lugar de usarse en combinación.
Entonces, ¿por qué la rutina lógicamente defectuosa sigue presente en pkcs11_add_provider() en /usr/bin/ssh si ahora hay un binario auxiliar adicional? ¿Y por qué la implementación parece sin cambios en el parche?
Bueno, resulta que el parche no añadía un binario auxiliar, sino que implicaba distribuir dos binarios ssh en macOS: /usr/bin/ssh y /usr/libexec/ssh-apple-pkcs11, ambos idénticos salvo por sus entitlements (usé Diaphora para validarlo):
Después de realizar algún análisis dinámico sobre el binario ssh parcheado, se añadió una comprobación adicional en la rutina start() (bastante grande) de ssh, que se utiliza, al hacer uso de las funciones pkcs11, para deducir si el binario se está ejecutando en el contexto de /usr/bin/ssh o de /usr/libexec/ssh-apple-pkcs11:
En el bloque verde, podemos observar una llamada a SecTaskCopyValueForEntitlement() (el valor pasado es "com.apple.private.security.clear-library-validation"), que luego (en el bloque naranja) se evalúa y provoca una llamada condicional a la rutina de re-ejecución si el entitlement "com.apple.private.security.clear-library-validation" no está presente para la tarea/proceso actual (bloque rojo).
Esto da como resultado que se utilice el binario ssh-apple-pkcs11, con menos entitlements, al cargar la biblioteca pkcs11 suministrada por el usuario (deshabilitando efectivamente las funciones relacionadas con el llavero de ssh), y que se utilice ssh cuando no se deban cargar bibliotecas no confiables (habilitando las funciones relacionadas con el llavero de ssh).
También podemos validar que este es el caso depurando el parcheado
Si depuramos el binario ssh parcheado (distribuido en macOS 22G74) y establecemos puntos de interrupción en execv(), podemos observar efectivamente una llamada a execv() al suministrar el indicador -I, que no ocurre cuando se lanza ssh sin usar funciones pkcs11:
Reporté el problema a Apple a través del programa Apple Security Bounty a finales de 2022, y posteriormente se corrigió en macOS Ventura 13.5, lo que dio lugar a CVE-2023-42829 (🎉).
Este artículo es una publicación independiente y no ha sido autorizado, patrocinado ni aprobado de ninguna otra forma por Apple Inc. macOS, iOS y iWork son marcas comerciales de Apple Inc.