Skip to main content

Como invadi contêineres Docker explorando vulnerabilidades do ImageMagick

Escrito por
Blog Header Hacking Docker

11 de março de 2021

0 minutos de leitura

E se eu dissesse que usar imagens Docker vulneráveis pode deixar você sob risco iminente e significativo de uma vulnerabilidade de segurança de injeção de comandos para invadir contêineres Docker que usam essa imagem vulnerável?

Neste artigo, vou mostrar um passo a passo de como invadir contêineres. Vamos explorar uma aplicação web baseada em Node.js que usa uma imagem-base oficial do Docker para Node.js, mas vulnerável.

Invasão de um contêiner com uma imagem vulnerável do Node.js

Para demonstrar essa vulnerabilidade, vou usar uma versão antiga do runtime Node.js com uma aplicação Fastify que redimensiona imagens para um tamanho específico. O redimensionamento é feito pela biblioteca ImageMagick, que oferece uma prática ferramenta de linha de comando chamada convert.

O ImageMagick é um conjunto de ferramentas de linha de comando e vínculos com linguagens de programação, comumente usado em aplicações web para processar imagens, por exemplo, convertendo-as de um formato para outro, redimensionando, cortando e muito mais.

Infelizmente, o ImageMagick já apresentou muitas vulnerabilidades de segurança ao longo dos anos. Uma delas é a famosa vulnerabilidade ImageTragick (CVE-2016-3714). Ela é classificada como validação inadequada de entrada, mas provas de conceito para explorá-la estão disponíveis publicamente desde 2016 e podem permitir a injeção remota de comandos.

Esta é uma história sobre a invasão de contêineres não por falta de boas práticas de segurança nem por dependências vulneráveis das aplicações Node.js, mas por causa de componentes de código aberto de terceiros que podem estar presentes em uma aplicação Node.js baseada em Docker.

A aplicação Node.js

Vamos começar pela imagem Docker que reúne a aplicação Node.js. Para simplificar, vou usar uma configuração de Dockerfile bem enxuta:

FROM node:6.1.0-wheezy 

RUN mkdir /usr/src/goof
COPY . /usr/src/goof
WORKDIR /usr/src/goof

RUN npm update
RUN npm install
CMD ["npm", "start"]

Este Dockerfile não segue as diretrizes seguras para criar imagens Docker e foi incluído apenas para simplificar. Confira nosso guia rápido com 10 boas práticas para conteinerizar aplicações web Node.js com Docker e conheça as práticas recomendadas de segurança.

A aplicação web Fastify disponibiliza uma rota **/**upload que aceita o envio de arquivos e executa o processo /usr/bin/convert com a imagem enviada para convertê-la para um tamanho específico. Em seguida, redireciona o usuário para uma página de sucesso.

fastify.post("/upload", (req, res) => {
  req.multipart(uploadFileHandler, err => {
    if (err) {
      fastify.log.error(err);
    }

    fastify.log.info("upload completed");
    fastify.log.info("commencing image resizing");

    child_process.execFile(
      "/usr/bin/convert",
      [FILE_OUTPUT, "-resize", "280x150", FILE_RESULT],
      () => {
        res.redirect("/result.html");
      }
    );
  });
});

Na verdade, o código usa uma API segura do Node.js para executar processos, sem permitir a concatenação de strings.

É uma boa prática iniciar shells do sistema e executar processos? Não exatamente — pelo menos, não sem uma boa justificativa. Se esse for um processo worker do Node.js que consome itens de uma fila ou recebe tarefas de um agendador, talvez faça sentido para esse caso de uso.

Mas quem somos nós para julgar, se um caso de uso parecido foi demonstrado ao vivo no evento Google I/O 2017?

Player de vídeo mostrando código do Firebase e do Google Cloud que baixa, redimensiona e envia imagens.

Vamos invadir um contêiner Docker

Comecei importando o projeto para o Snyk, que detecta automaticamente os arquivos de manifesto compatíveis. Neste caso, são o package.json da aplicação e o Dockerfile.

Como você pode ver, até mesmo a versão mais recente da imagem Node.js 6 (node:6-stretch) contém, por padrão, 866 vulnerabilidades de segurança. Felizmente, usamos o Snyk, que recomenda várias imagens-base alternativas capazes de melhorar a segurança da aplicação de pelo menos duas maneiras:

  1. menos vulnerabilidades, o que reduz a superfície de ataque.

  2. imagens-base em que o próprio runtime Node.js não tem vulnerabilidades. Esse é um benefício muito importante, mas que muitas vezes passa despercebido, de seguir as recomendações de imagens-base.

Visão geral do projeto no Snyk, mostrando detalhes do Dockerfile e uma tabela com recomendações de atualização da imagem base, vulnerabilidades e níveis de gravidade.

Vamos transformar a vulnerabilidade de segurança ImageTragick, presente na versão do ImageMagick que está na imagem do contêiner, em um ataque de injeção remota de comandos.

A exploração usa arquivos de imagem especialmente criados para contornar a funcionalidade de análise do recurso delegates da biblioteca ImageMagick. Esse recurso executa comandos do sistema associados a instruções dentro do arquivo de imagem. Ao escapar do contexto de entrada esperado, um invasor consegue injetar comandos do sistema.

Este repositório do Git inclui uma prova de conceito para execução remota de comandos. Ela baixa o netcat para obter um shell reverso em distribuições que não incluem o netcat por padrão, como o Debian wheezy.

Veja como é o payload da prova de conceito no arquivo de imagem rce1.jpg:

push graphic-context
viewbox 0 0 640 480
fill 'url(https://127.0.0.0/oops.jpg"|touch "rce1)'
pop graphic-context

O arquivo de imagem está sob controle do usuário. Assim, um invasor mal-intencionado poderia criar esse payload, que executa um comando compatível com sistemas operacionais do tipo UNIX para criar um arquivo vazio — neste caso, touch rce1.

Depois de criar a imagem do contêiner, podemos executá-la:

docker run --rm --name rce rce

Nossa aplicação web simples permite enviar um arquivo:

Página simples exibindo “olá”, um controle para enviar imagens e um botão Redimensionar para converter fotos em 280×150 pixels.

Ao clicar no botão Resize para processar o arquivo rce1.jpg, a injeção de comandos é acionada.

Vamos nos conectar à aplicação que está em execução no contêiner Docker para validar esse ataque. Como podemos ver, um novo arquivo chamado rce1.jpg foi criado no diretório raiz da aplicação Node.js:

Terminal exibindo comandos de contêiner Docker e uma listagem de diretório de um projeto Node.js, incluindo package.json, Dockerfile, exploits e um arquivo destacado.

Resumo da invasão de contêineres

Você provavelmente não vai se surpreender com a quantidade de vulnerabilidades de segurança presentes em várias imagens Docker. Nós também não, já que observamos vulnerabilidades nas 10 imagens Docker mais populares do Docker Hub, como mostra nosso relatório O estado da segurança do código aberto.

Gráfico de barras intitulado “Vulnerabilidades em imagens oficiais de contêiner”, comparando a quantidade de vulnerabilidades em imagens de Node, PostgreSQL, Nginx e outras, em 2018 e 2019.

O ataque que demonstramos contorna completamente todas as convenções de programação segura e vai além da segurança do runtime Node.js ou das dependências de módulos de código aberto incluídas na aplicação. Isso reforça a importância de proteger suas imagens Docker. O Snyk foi criado justamente para isso!

Ao destacar vulnerabilidades priorizadas, o Snyk recomenda medidas de correção, como trocar por outras imagens-base:

Tabela com recomendações de atualização de imagens base do Node.js, indicando a quantidade de vulnerabilidades e as classificações de gravidade alta, média e baixa.

Se quiser recriar o ataque passo a passo, siga as instruções do README no repositório de código aberto. Ele também explica como realizar um ataque de shell reverso remoto com base nessa vulnerabilidade do ImageTragick.

E agora?

Se os repositórios do seu projeto com aplicações Docker forem públicos ou privados, você pode usar o plano gratuito do Snyk para testar suas imagens Docker e corrigir vulnerabilidades de segurança conhecidas. Aproveite também para analisar e corrigir suas aplicações Node.js!

O Dockerfile apresentado aqui e no repositório de código aberto associado não é recomendado por não seguir práticas de segurança. Em vez disso, confira algumas boas práticas de segurança para Docker nas publicações abaixo:

Segurança de contêineres que prioriza os desenvolvedores

O Snyk encontra e corrige automaticamente vulnerabilidades em imagens de contêiner e workloads do Kubernetes.