
Análise de uma vulnerabilidade lógica no cliente SSH do macOS que leva à exposição da frase secreta do cliente a um atacante local
Este documento apresenta tanto uma análise de uma vulnerabilidade lógica identificada no binário ssh no macOS (minha primeira vulnerabilidade de software!) quanto uma análise de patch demonstrando como a Apple corrigiu a vulnerabilidade. Reportei o problema à Apple através do programa Apple Security Bounty no final de 2022, que posteriormente foi corrigido no macOS Ventura 13.5 e resultou no CVE-2023-42829 (🎉). O problema leva a que frases secretas SSH salvas no Keychain 'Login' local do usuário no macOS (no grupo de acesso com.apple.ssh.passphrases) sejam expostas em texto simples a um atacante local.
Aviso:
Este relatório é fornecido apenas para fins educacionais, tendo sido seguidos os processos de divulgação responsável. A análise é oferecida como está, e qualquer publicação ou uso posterior destas informações deve aderir às diretrizes de divulgação responsável.
Fluxo de alto nível corrigido
Fluxo de alto nível não corrigido
com.apple.private.security.clear-library-validation| Hardware | Software do SO |
|---|---|
| MacBook Pro M1 2021 | /usr/bin/ssh @ macOS Ventura build 13.0.1 (22A400) |
A seguinte prova de conceito ilustra a exploração bastante simples da vulnerabilidade, passando uma biblioteca dinâmica para a flag -I do binário ssh:
jamesd@local build % ssh -I /Users/jamesd/rome.dylib [email protected]
SSH Private Key Passphrase -> 'SuperSecret!!@@$'
Vamos discutir como isso foi descoberto e por que aconteceu!
Era uma vez, eu estava usando o binário ssh para algo inofensivo e digitei errado a flag -i (usada para passar um arquivo de identidade SSH) com a flag -I, e me deparei com a seguinte saída padrão:
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), ...
...
Depois de verificar os entitlements do binário ssh...
jamesd@local build % ldid -e /usr/bin/ssh # despejar os entitlements do binário /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>
...meu interesse foi despertado – o binário tem entitlements para ler de um grupo de acesso protegido do keychain (com.apple.ssh.passphrases), possui o entitlement com.apple.private.security.clear-library-validation, e o ssh está tentando fazer dlopen() da minha chave privada?
Acontece que o ssh suporta autenticação em um sistema remoto através de algo chamado pkcs11, um padrão para operações criptográficas em módulos de segurança de hardware (HSMs) (https://docs.aws.amazon.com/cloudhsm/latest/userguide/pkcs11-library.html).
Não precisamos nos preocupar com os detalhes específicos do pkcs11 para os fins deste artigo, além do fato de que o cliente fornece uma biblioteca pkcs11 (biblioteca dinâmica) para ssh -I.
com.apple.private.security.clear-library-validationEmbora conceitualmente equivalente, o com.apple.private.security.clear-library-validation difere do entitlement equivalente anterior (com.apple.security.cs.disable-library-validation), pois o com.apple.private.security.clear-library-validation exige que você faça a chamada de sistema csops() passando CS_OPS_CLEAR_LV para controlar a ativação/desativação da validação de bibliotecas, mantendo maior controle sobre a integridade do processo em tempo de execução (em comparação com com.apple.private.security.clear-library-validation, que presumivelmente permitiria carregar qualquer biblioteca sem controle em tempo de execução). (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/)
Como csops(CS_OPS_CLEAR_LV) é chamado antes de fazer dlopen() da nossa biblioteca, nossa biblioteca simplesmente carrega e o construtor é executado, permitindo fornecer uma biblioteca dinâmica maliciosa disfarçada de biblioteca pkcs11 e executar código a partir do contexto de /usr/bin/ssh e utilizar o entitlement keychain-access-groups.
Talvez o patch envolvesse verificações realizadas no binário antes de chamar csops() para verificar identidades de assinatura confiáveis específicas?
Não!
Após o lançamento do patch (22G74), comparei a implementação insegura de pkcs11_add_provider() (o método no qual a chamada para dlopen() é feita) e parecia idêntica à versão do patch – mas há um entitlement faltando no /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>
Mas certamente a Apple não removeu o suporte a pkcs11 do SSH? Tentei re-explorar a vulnerabilidade usando meu PoC e descobri que, embora minha biblioteca fosse carregada, ela não conseguia mais ler do Grupo de Acesso do Keychain e parecia estar executando no contexto de /usr/libexec/ssh-apple-pkcs11 (um binário que eu não tinha visto antes) que possui os seguintes 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>