ssh-keysign-pwn: Lendo arquivos de propriedade raiz por meio de um bug de lógica ptrace
ssh-keysign-pwn: Um bug lógico de seis anos na função
__ptrace_may_access()do kernel, relatado pela Qualys e corrigido por Linus Torvalds em 14 de maio de 2026. Usuários sem privilégios podem ler arquivos de propriedade do root, incluindo chaves privadas do host SSH e/etc/shadow. Todos os kernels pré-31e62c2ebbfdafetados.
Outro
Maio de 2026 não foi gentil com o kernel Linux. Dirty Frag, Fragnesia e agora—ssh-keysign-pwn—divulgado pela Qualys em 14 de maio e corrigido por Linus Torvalds no mesmo dia.
Este é diferente da família “copiar algo para o cache da página”. É um bug lógico puro em __ptrace_may_access(), a função do kernel que decide se um processo pode inspecionar outro.
| Métrica | Valor |
|---|---|
| Relatado por | Assessoria de Segurança Qualys |
| Corrigido por | Linus Torvalds |
| Corrigir confirmação | 31e62c2ebbfd |
| Esperado por | ~6 anos |
| Primeiro sinalizado por | Jann Horn (Google), outubro de 2020 |
| PoC publicado por | _Doente |
| Impacto | Leia arquivos de propriedade do root como usuário sem privilégios |
| Explorar complexidade | 100–2.000 spawns por roubo bem-sucedido |
Como funciona
O bug reside em __ptrace_may_access(). Esta função é o guardião da introspecção do processo – ela verifica se um processo tem permissão para bisbilhotar o estado de outro processo.
Há um caso especial: quando task->mm == NULL (o processo de destino não tem descritor de memória - acontece quando um thread está saindo ou para threads do kernel), a função ignora totalmente a verificação de dumpable.
Aqui está o caminho do código que torna isso explorável:
- Um processo chama
do_exit()para encerrar do_exit()executaexit_mm()primeiro - isso destrói o descritor de memória (mm)- Em seguida, ele executa
exit_files()— mas os descritores de arquivo ainda estão ativos neste ponto - Com
task->mm == NULL, mas os descritores de arquivo ainda abertos,pidfd_getfd(2)pode roubar esses fds se o uid do chamador corresponder
Normalmente, as verificações de acesso ptrace impediriam que um processo menos privilegiado alcançasse os identificadores de arquivo abertos de um processo raiz. Mas o desvio mm == NULL elimina a verificação descartável e pidfd_getfd(2) faz o resto.
Esta é uma corrida TOCTOU clássica, mas a janela é ampla o suficiente para que o PoC acerte em 100–2.000 tentativas.
Explorar alvos
Duas ferramentas foram publicadas:
sshkeysign_pwn — tem como alvo ssh-keysign, um binário auxiliar que assina desafios de autenticação de host. O binário abre arquivos de chave do host SSH (/etc/ssh/ssh_host_{ecdsa,ed25519,rsa}_key, modo 0600) antes de chamar permanently_set_uid() para eliminar privilégios. Em seguida, ele verifica se EnableSSHKeysign está definido em sshd_config — caso contrário, ele sai com a chave fds ainda aberta. Este design está no OpenSSH desde 2002.
chage_pwn — alvos chage -l <user>. O binário abre /etc/shadow via spw_open(O_RDONLY) e depois chama setreuid(ruid, ruid) — eliminando todos os privilégios, pois ambos os argumentos são o uid real. Na janela de corrida entre a queda do privilégio e o fechamento do arquivo, o fd pode ser roubado.
Linha do tempo
| Data | Evento |
|---|---|
| ~2020 | Jann Horn identifica o padrão de roubo de FD e propõe um patch (não mesclado) |
| 14/05/2026 | Qualys relata a vulnerabilidade; Linus Torvalds confirma a correção (31e62c2ebbfd) |
| 14/05/2026 | Brad Spengler (@spendergrsec) publica análise |
| 14/05/2026 | _SiCk publica exploits PoC no GitHub |
| 15/05/2026 | Qualys envia divulgação para oss-security |
A lacuna entre a proposta de patch de Jann Horn para 2020 e o relatório de 2026 da Qualys é notável. A correção foi elaborada há cinco anos, mas nunca foi implementada no upstream.
A correção
Linus confirmou a correção como commit 31e62c2ebbfd. Sua mensagem de commit observou:
“temos um caso especial estranho: ptrace_may_access() usa ‘dumpable’ para verificar várias outras coisas de forma totalmente independente do MM (normalmente usando explicitamente sinalizadores como
PTRACE_MODE_READ_FSCREDS), inclusive para threads que não possuem mais uma VM (e talvez nunca tenham tido, como a maioria dos threads de kernel). Não é para isso que esse sinalizador foi projetado, mas é o que é.”
O patch ajusta o comportamento do ptrace para lidar adequadamente com o caso mm == NULL. Ele fecha efetivamente a janela de bypass.
Verifique seu kernel
# Check if your kernel has the fixuname -r# Fixed kernels have commit 31e62c2ebbfd included# All kernels before 2026-05-14 are vulnerableO que torna este interessante
Três coisas se destacam:
-
Foi consertado no mesmo dia em que foi relatado. Linus resolveu isso em horas. Isso é incomumente rápido para uma correção do kernel, sugerindo que o patch foi simples assim que o bug foi compreendido.
-
Jann Horn descobriu isso em 2020. A comunidade tinha uma proposta de patch nos arquivos da lista de discussão por cinco anos. A forma do roubo FD era conhecida, mas ninguém a forçou.3. Os alvos são antigos. O padrão de saída do fd de
ssh-keysigndata de 2002. A sequênciaspw_open+setreuiddechageé igualmente de longa data. Este não é um bug no SSH ou shadow-utils - é um bug do kernel que transforma os padrões de manipulação de fd existentes em binários privilegiados.
Esta também é a terceira grande vulnerabilidade do kernel Linux em maio de 2026, depois de Copy Fail e Dirty Frag. O ritmo de divulgação está se acelerando — em parte porque as ferramentas de auditoria assistidas por IA estão encontrando bugs antigos com mais rapidez e, em parte, porque o ecossistema de divulgação está se fragmentando (embargos sendo quebrados, PoCs lançados antes dos patches).
Referências
- Confirmação de correção do kernel: 31e62c2ebbfd
- Relatório Phoronix: A vulnerabilidade mais recente do Linux permite a leitura de arquivos de propriedade raiz por usuários sem privilégios
- GitHub PoC: 0xdeadbeefnetwork/ssh-keysign-pwn
- 9to5Linux: Falha no kernel Linux de seis anos permite que usuários não privilegiados leiam arquivos de propriedade de root
- Postagem Qualys oss-security: Bug lógico em __ptrace_may_access()
- Patch de 2020 de Jann Horn: lore.kernel.org