Skip to main content

A vulnerabilidade “Dirty Pipe” do Linux e suas aplicações em contêineres (CVE-2022-0847)

Escrito por
blog feature security alert purple

9 de março de 2022

0 minutos de leitura

O que é a vulnerabilidade “Dirty Pipe”? (CVE-2022-0847)

Recentemente, CVE-2022-0847 foi criada para descrever uma falha no kernel do Linux que pode ser explorada para permitir que qualquer processo modifique arquivos, independentemente das permissões ou da propriedade. A comunidade de segurança deu à vulnerabilidade o nome “Dirty Pipe” por sua semelhança com a “Dirty COW”, uma vulnerabilidade de escalonamento de privilégios relatada em CVE-2016-5195, e porque a falha está na implementação do pipeline do kernel. Há várias referências a essa vulnerabilidade documentadas no banco de dados de vulnerabilidades Snyk Intel, caso você queira se aprofundar.

Neste artigo, vamos falar sobre os riscos aos quais suas cargas de trabalho em contêineres estão expostas e analisar em detalhes como o conteúdo de imagens de contêineres e outros arquivos normalmente somente leitura pode ser modificado por um agente mal-intencionado, independentemente do UID e das permissões do sistema de arquivos.

Para começar, veja o que você pode fazer agora para proteger seus sistemas contra a Dirty Pipe.

Em resumo: atualize seus hosts Linux

A única correção conhecida para essa vulnerabilidade é atualizar seus hosts Linux para uma das seguintes versões do kernel:

  • 5.16.11

  • 5.15.25

  • 5.10.102

Não há outras opções de mitigação capazes de proteger seus sistemas caso um agente mal-intencionado consiga acesso ao seu ambiente.

Como a Dirty Pipe afeta suas imagens de contêineres

Noções básicas sobre imagens de contêineres

Diagrama em camadas mostrando uma camada R/W acima de várias camadas identificadas e de uma base, com hashes SHA-256 em cada camada.

Uma imagem de contêiner é basicamente formada por um conjunto de camadas sobrepostas. Quando um contêiner é iniciado, o mecanismo de execução (Docker, containerD, cri-O etc.) combina essas camadas e apresenta o resultado agregado ao processo como sistema de arquivos. Essas camadas são sempre somente leitura, e qualquer alteração é feita em uma camada de leitura e gravação, criada especificamente para cada instância do contêiner seguindo o padrão copy-on-write (COW). Essa camada de leitura e gravação é temporária e é destruída quando o contêiner é removido do sistema. Também é possível omitir essa camada iniciando o contêiner no modo somente leitura, tornando-o efetivamente imutável.

É comum recomendar que os processos dos contêineres sejam executados como usuários sem privilégios e que os sistemas de arquivos raiz sejam somente leitura. Essas medidas dificultam muito que um agente mal-intencionado explore vulnerabilidades da aplicação para ampliar o ataque, pois tornam muito mais difícil incluir código ou scripts próprios no sistema de arquivos do contêiner. Confira nosso guia de segurança para contêineres para conhecer outras práticas recomendadas.

Explorando a Dirty Pipe em um contêiner

Os detalhes técnicos de como a vulnerabilidade Dirty Pipe funciona estão fora do escopo deste artigo — consulte o artigo original sobre a descoberta para ver todos os detalhes —, mas, em linhas gerais, é possível executar uma sequência relativamente simples de etapas para alterar o conteúdo de praticamente qualquer arquivo, mesmo que suas permissões e/ou configurações de propriedade impeçam esse tipo de ação. Por exemplo, usando o código de prova de conceito write_anything do artigo mencionado acima, vamos criar uma imagem com este Dockerfile:

Dockerfile com realce de sintaxe mostrando uma compilação em várias etapas que compila write_anything.c e copia o binário para uma imagem Debian slim

Em seguida, executamos o contêiner no modo somente leitura e usamos o executável write_anything para alterar /etc/passwd.

Demonstração no terminal mostrando um contêiner Docker sem privilégios de root sobrescrevendo /etc/passwd com “OH SNAP!”

Eu não deveria ter conseguido fazer isso, porque o arquivo é somente leitura, pertence ao root e está em um sistema de arquivos raiz somente leitura.

Agora, vamos encerrar e remover esse contêiner, iniciar outro e verificar o arquivo /etc/passwd no novo contêiner.

Terminal exibindo um comando de contêiner Docker, o ID do contêiner e a saída alterada de /etc/passwd, destacados por setas.

A alteração no arquivo persistiu porque o conteúdo real da camada da imagem, que é somente leitura no sistema host, foi modificado pelo processo do contêiner anterior. Essa alteração permanecerá nesse host até que a imagem seja removida e/ou substituída.

Camada de imagem de baixo nível antes de executar o exploit

Comandos de terminal comparam o registro de data e hora de um sistema de arquivos overlay do Docker com o conteúdo original e não modificado do arquivo /etc/passwd.

Camada de imagem de baixo nível depois de executar o exploit

Saída do terminal mostrando o horário do arquivo inalterado, apesar do conteúdo modificado de /etc/passwd em uma camada de sobreposição do Docker

Na verdade, se você tiver vários contêineres em execução no mesmo host, uma alteração como essa poderá ser vista imediatamente por qualquer contêiner que compartilhe essa camada base da imagem (com exceção daqueles que já tenham modificado o mesmo arquivo em sua própria camada de leitura e gravação).

Três contêineres monitorando seus arquivos /etc/passwd com watch enquanto um quarto contêiner executa o exploit write_anywhere.

A tela do terminal compara a saída repetida de /etc/passwd em três hosts, com um prompt do shell do Ubuntu visível abaixo.

A Dirty Pipe coloca em risco todos os volumes montados do host

Personagem em pixel art usando uma capa roxa, com o texto de meme “ALL YOUR FILES ARE BELONG TO DIRTY PIPE.”

Como você talvez já saiba, montar volumes do host (também chamados de bind mounts) nos seus contêineres nunca é uma boa ideia. Em condições normais, essa recomendação existe porque uma configuração incorreta simples pode conceder ao contêiner acesso não intencional para modificar arquivos no host. A Dirty Pipe agrava o problema: volumes montados do host ficam vulneráveis a esse exploit mesmo quando são montados com a flag :ro.

Terminal mostrando um contêiner Docker com um diretório montado como somente leitura e um comando que modifica o arquivo.txt com sucesso, apesar da restrição.

Escalonamento de privilégios com a Dirty Pipe

Um dos exemplos de prova de conceito do exploit que está circulando usa essa falha para obter privilégios elevados: modifica um binário existente com o bit suid habilitado para contornar as proteções normais contra esse tipo de ação. Isso não é um problema exclusivo de contêineres, mas o exploit pode ser usado dentro deles. Por exemplo, em tese, um invasor poderia explorar uma vulnerabilidade de execução remota de código (RCE) em uma aplicação e injetar esse código de prova de conceito para obter acesso root ao contêiner. Na prática, isso contorna as configurações do contêiner e/ou do Kubernetes que limitam esse comportamento.

Há medidas de mitigação?

Como mencionamos acima, atualizar os sistemas host é a única forma conhecida de proteger seus sistemas contra essa vulnerabilidade. As medidas de mitigação dos mecanismos de contêineres e do Kubernetes operam em um nível alto demais para oferecer muita proteção contra um exploit no nível do kernel como esse.

Não basta atualizar nossas imagens base?

Infelizmente, não. A falha não está no sistema de arquivos das imagens base, mas no kernel dos servidores host, compartilhado por todos os contêineres em execução. Você pode confirmar isso executando uname -a em vários contêineres com imagens base diferentes: todos informarão a versão do kernel do host.

Terminal exibindo comandos Docker executados com CentOS 7, CentOS 8 e Alpine, mostrando a saída do comando uname em cada contêiner.

E as configurações SecurityContext do Kubernetes?

As configurações SecurityContext do Kubernetes, como readonlyRootFilesystem:true, runAsNonRoot:true, runAsUser: e AllowPrivilegeEscalation:false, são ineficazes porque qualquer usuário pode explorar essa vulnerabilidade e contornar essas configurações.

Isso não significa que você deva deixar de usar essas configurações. Pelo contrário: elas são ótimos exemplos de defesa em profundidade e protegem seus clusters contra muitos outros tipos de ataque.

Na verdade, temos um guia rápido de segurança do Kubernetes que explica as configurações SecurityContext e por que você deve usá-las para reforçar a segurança das implantações das suas aplicações no Kubernetes. Você também pode verificar seus manifestos do Kubernetes com as ferramentas gratuitas de análise de IaC da Snyk para encontrar e corrigir configurações incorretas como essas. Acesse o link abaixo para começar a usar hoje mesmo.

Conclusão

Se ainda não fez isso, atualize seus hosts imediatamente. Se algum dos tópicos acima despertou seu interesse, confira os links a seguir para se aprofundar.

Proteja a infraestrutura desde a origem

A Snyk automatiza a segurança e a conformidade de IaC nos fluxos de trabalho e detecta recursos com configurações divergentes ou ausentes.