flawopen.com/Teardowns/cve-2024-21626-runc-container-breakout-leaky-vessels
CVE-2024-21626: como o runc corrigiu o escape Leaky Vessels
A CVE-2024-21626 combinou um vazamento interno de descritor no runc com a ausência de uma verificação do limite após chdir. Conforme o caminho de inicialização, o contêiner podia começar com um diretório de trabalho no namespace do host ou alcançar arquivos do host por um caminho relacionado a exec.[1], [2], [3]
O runc prepara um cômodo isolado para o contêiner. Um handle interno vazado podia fazer o diretório de trabalho escolhido apontar para fora desse cômodo; a checagem final do limite e o fechamento dos handles internos impedem essa passagem.[1], [2]
Conceitos Centrais e Termos
runc- O runtime OCI que cria e inicia contêineres.[1]
Descritor de arquivo interno- Um handle do sistema operacional para um arquivo ou diretório aberto. Um descritor vazado pode manter acesso entre processos se continuar aberto durante a execução de um filho.[1], [3]
cwd- O diretório de trabalho do processo. No Linux, Getwd pode retornar ENOENT quando ele está fora da raiz do namespace de montagem.[1], [2]
Escape de contêiner- Um caminho pelo qual um processo do contêiner alcança recursos do host fora do limite de isolamento.[1]
Mapeamentos de frameworks
- Referência CWE para a fraqueza descrita neste guia: CWE-403
Análise de Causa Raiz
Antes do runc 1.1.12, descritores internos podiam vazar para o runc init. Eles eram internos ao runc init, não simplesmente herdados pelo processo do contêiner. Nos ataques 1 e 2, o diretório de trabalho configurado ou escolhido podia resolver por um handle do namespace de montagem do host; o runc não verificava após chdir se o diretório final permanecia dentro da raiz do contêiner. Variantes separadas usavam o tratamento de caminhos de execve para sobrescrever um binário do host. A versão 1.1.12 adicionou quatro camadas: validar o diretório com o comportamento Getwd/ENOENT do Linux, fechar descritores internos imediatamente antes de execve, corrigir vazamentos identificados e marcar descritores que não sejam de stdio como close-on-exec antes de iniciar runc init.[1], [2], [3], [4]
CVE-2024-21626: variantes de ataque e proteções do runc 1.1.12
| Variante do aviso | Condições e limite necessários | Correção ou medida temporária relevante |
|---|---|---|
Ataque 1 — imagem maliciosa via runc run | Um usuário privilegiado precisa ser induzido a iniciar uma imagem não confiável. O diretório de trabalho configurado pode resolver fora do namespace de montagem do contêiner. | A verificação unix.Getwd() após chdir falha quando o cwd está fora da raiz do namespace. Como medida temporária, permita apenas / como cwd para imagens não confiáveis. |
Ataque 2 — processo no contêiner com runc exec | Um processo já dentro do contêiner precisa antecipar a escolha de um cwd diferente de / feita pelo administrador com runc exec --cwd e preparar o caminho relevante antes da execução. | A mesma verificação de cwd após chdir bloqueia a execução fora do contêiner. Até aplicar a correção, restrinja --cwd a /. |
| Ataque 3a — sobrescrita de binário do host ao iniciar imagem | É uma variação do ataque 1 e mantém a exigência de um usuário privilegiado iniciar a imagem maliciosa; a variante cruza o limite de execve até um binário do host. | Feche os descritores internos do runc imediatamente antes de execve, corrija os vazamentos identificados e defina O_CLOEXEC. Atualize para runc 1.1.12 ou versão corrigida pelo fornecedor. |
Ataque 3b — sobrescrita de binário do host via runc exec | Esta variação adapta o ataque 2 e parte de um processo malicioso em um contêiner em execução que o invasor pode afetar; ainda é necessário que um administrador privilegiado faça uma operação normal de runc exec nesse contêiner. O aviso diz que isso exige menos interação do usuário que o caminho por imagem. | As proteções de fechamento antes de execve e close-on-exec tratam esse caminho distinto. Os mantenedores não apontam uma alternativa prática; atualize. |
Fluxo de Ataque Passo a Paso
Impacto e condições
O aviso documenta acesso ao sistema de arquivos e controle do host em quatro variantes. Ataques por imagem exigem que um usuário privilegiado execute uma imagem não confiável. O ataque 3b ainda exige que um administrador faça uma operação normal de runc exec em um contêiner que o invasor possa afetar.[1]
Código-Fonte: Vulnerável vs. Seguro
// Comportamento pré-correção simplificado; não é trecho literal do runc
if err := os.Chdir(config.Cwd); err != nil {
return err
}
// Faltava verificar se o cwd final está dentro da raiz do namespace
// Verificação de limite do runc 1.1.12 (trecho upstream)
if wd, err := unix.Getwd(); errors.Is(err, unix.ENOENT) {
return errors.New("cwd está fora da raiz do namespace de montagem do contêiner")
} else if err != nil {
return fmt.Errorf("não foi possível validar a segurança do cwd: %w", err)
} else if !filepath.IsAbs(wd) {
return fmt.Errorf("cwd não é absoluto: %q", wd)
}
Lista de Verificação de Segurança para Engenharia
- ✓Atualize o runc para 1.1.12 ou uma versão posterior suportada pelo fornecedor e confirme a versão efetiva em cada imagem de nó.[1], [4]
- ✓Trate imagens não confiáveis como solicitações de execução de código; restrinja quem pode iniciá-las via Docker, Kubernetes e pipelines de build.[1]
- ✓Em runtimes, valide o diretório após chdir com a verificação de raiz de namespace suportada pela plataforma e falhe de forma segura diante de erros.[1], [2]
- ✓Use O_CLOEXEC nos descritores internos e feche descritores não-stdio no último ponto seguro antes de exec, considerando os descritores próprios do runtime.[1], [3]
- ✓Não trate user namespaces como substituto do patch do runc; use-os como camada adicional de isolamento quando compatíveis.[1]