Pesquisa

Contêineres em chamas: do escape para o host aos ataques à cadeia de suprimentos

Introdução

As infraestruturas modernas utilizam amplamente a conteinerização para implantar aplicações, escalar serviços e construir plataformas em nuvem. O uso de Docker, Kubernetes e outras tecnologias tornou-se padrão para a automação eficiente de ambientes corporativos. No entanto, junto com a popularidade crescente dos contêineres, cresce também o interesse dos invasores por essa tecnologia, conforme identificado em nossa pesquisa sobre ameaças cibernéticas complexas. Por exemplo, o grupo APT TeamPCP comprometeu, em um de seus ataques recentes, o Checkmarx KICS no contexto de várias cadeias simultâneas para diferentes vetores, incluindo a infecção do repositório Docker Hub para o roubo posterior de segredos do Kubernetes e de outras informações sensíveis. As imagens infectadas distribuíam um stealer, carregado durante o processo de scan do KICS.

Atualmente, os ataques a ambientes de contêineres constituem cenários completos e multiestágios, que incluem ataques à cadeia de suprimentos, roubo de segredos do Kubernetes, abuso da API de orquestração e tentativas de escape do contêiner. Neste artigo, analisamos os principais vetores de ataque a contêineres que se mantêm entre os mais relevantes na atualidade.

Princípios da conteinerização

Um contêiner é um ambiente isolado para a execução de código, que permite processá-lo de forma separada e independente. Diferentemente de uma máquina virtual, o contêiner compartilha um único kernel com o sistema operacional.

Para isolar o ambiente, o contêiner utiliza um namespace de processos separado e um sistema de arquivos virtual. Seus recursos são limitados e compartilhados com o sistema host. O isolamento de contêineres é construído sobre mecanismos do kernel Linux como namespaces, cgroups, capabilities e seccomp.

O comprometimento de um contêiner pode auxiliar os invasores a atingir seus objetivos no sistema host. A seguir, analisamos os vetores atuais que são relevantes para a arquitetura de implementação de contêineres e sua infraestrutura.

Vetores de ataque atuais

Apresentamos, a seguir, os principais e mais atuais vetores de ataque a ambientes de contêineres, ativamente utilizados por invasores:

  • exploração de vulnerabilidades do sistema host e dos componentes do contêiner runtime;
  • ações maliciosas executadas dentro de um contêiner comprometido;
  • escape do contêiner com comprometimento subsequente do nó;
  • exploração de erros de configuração e uso inseguro da API de conteinerização e orquestração;
  • ataques à cadeia de suprimentos, incluindo a infecção de imagens de contêineres e o comprometimento de processos de CI/CD.

Cada um dos vetores listados pode ser empregado isoladamente ou como parte de um ataque complexo e multiestágio. Na prática, os invasores raramente se limitam ao comprometimento de um único contêiner: o objetivo principal costuma ser a obtenção de acesso ao cluster Kubernetes, a sistemas de armazenamento de segredos ou a outros componentes críticos do ambiente. Por isso, a proteção da infraestrutura de contêineres exige uma abordagem abrangente, que contemple controle de configurações, proteção do ambiente de execução, monitoramento de atividade e garantia da segurança da cadeia de suprimentos de software. A seguir, analisamos em detalhe cada um dos vetores apresentados.

Exploração de vulnerabilidades do sistema host

Como o contêiner não dispõe de um sistema operacional isolado próprio, vulnerabilidades que afetam o kernel Linux ou os componentes do ambiente de execução permanecem relevantes também na exploração a partir do contêiner.

Qualquer vulnerabilidade que possibilite elevar privilégios, executar código arbitrário ou violar os mecanismos de isolamento pode ser potencialmente explorada por um invasor após o comprometimento do contêiner. A exploração bem-sucedida dessas vulnerabilidades pode levar à saída do contêiner, ao comprometimento do nó Kubernetes ou de todo o cluster, à movimentação lateral pela infraestrutura, ao roubo de segredos e à execução de ações maliciosas, podendo culminar na negação total de serviços. Cabe ressaltar que a simples presença da vulnerabilidade nem sempre resulta em comprometimento, pois em alguns casos a exploração exige parâmetros de configuração ou privilégios adicionais.

A seguir, apresentamos exemplos de algumas vulnerabilidades usadas em ataques a ambientes de contêineres.

  • CVE-2019-5736 é uma das vulnerabilidades mais conhecidas e representativas relacionadas à conteinerização. Ela afetava o contêiner runtime runC e possibilitava que um invasor, já com acesso dentro do contêiner, executasse código arbitrário no sistema host com privilégios de root. A causa da vulnerabilidade residia no tratamento incorreto, pelo runC, do descritor de arquivo do seu próprio executável por meio do mecanismo /proc/self/exe. Ao iniciar o contêiner, o processo runC era executado temporariamente em seu contexto, permanecendo simultaneamente um processo do sistema host, o que permitia que o invasor obtivesse acesso ao binário do runC e sobrescrevesse seu conteúdo.
  • CVE-2022-0492 é uma vulnerabilidade crítica do kernel Linux que possibilitava realizar o escape do contêiner e executar comandos arbitrários no sistema host. O problema estava relacionado à verificação incorreta de privilégios no tratamento do mecanismo cgroups release_agent. A vulnerabilidade tornou-se especialmente perigosa para infraestruturas de contêineres, pois permitia que um invasor, já capaz de executar código dentro do contêiner, transpusesse os limites do isolamento e obtivesse controle sobre o sistema host.
  • CVE-2024-21626 é uma vulnerabilidade crítica no runC que possibilitava a um invasor obter acesso ao sistema de arquivos do host a partir do contêiner e, em determinados cenários, até realizar um escape completo dele. A principal causa do problema residia no tratamento incorreto, pelo runC, dos descritores de arquivo e do diretório de trabalho atual do processo ao iniciar contêineres e executar comandos por meio de docker exec ou mecanismos semelhantes.

Ações maliciosas dentro do contêiner

Às vezes, para alcançar seus objetivos, o invasor não precisa explorar cadeias de ataque complexas que envolvam escape do contêiner, comprometimento do cluster Kubernetes ou movimento lateral pela infraestrutura. Em muitos casos, o próprio contêiner já contém dados e recursos valiosos para o invasor, como, por exemplo:

  • credenciais de usuários e serviços;
  • chaves de API;
  • tokens de autorização;
  • chaves SSH;
  • variáveis de ambiente com segredos;
  • tokens de ServiceAccount no Kubernetes;
  • arquivos de configuração;
  • dados de serviços de aplicação ou de bancos de dados.

Esses dados costumam ficar acessíveis, sobretudo, devido a erros de configuração ou particularidades dos processos. Por exemplo, segredos podem ser transmitidos por variáveis de ambiente, inseridos em imagens Docker na etapa de build ou montados dentro do contêiner. Em ambientes Kubernetes, os tokens de ServiceAccount montados automaticamente, que possibilitam a interação com a API do Kubernetes, representam um alvo de interesse particular para os invasores.

Até mesmo o comprometimento de um único contêiner costuma dar ao invasor recursos suficientes para ações posteriores, como obter acesso a serviços externos, comprometer a infraestrutura em nuvem, roubar dados de usuários, executar operações em nome de um serviço confiável e conquistar persistência na infraestrutura. Além do roubo de dados, os invasores podem utilizar o contêiner comprometido como base para a realização de atividades maliciosas. Por isso, a segurança da infraestrutura de contêineres não se limita à proteção contra escapes. Até mesmo um contêiner isolado, que contenha dados sensíveis ou tenha acesso a serviços internos, pode se tornar um ponto de comprometimento da infraestrutura.

Neste vetor, costumam ser aplicadas abordagens e técnicas relevantes não apenas para ambientes de contêineres, mas também para sistemas tradicionais. Após obter acesso ao contêiner, o invasor geralmente se depara com um ambiente Linux completo, no qual podem ser usados métodos padrão de pós-exploração, coleta de informações e estabelecimento de persistência.

Aprofundamos a análise sobre erros de configuração de contêineres e outras práticas inseguras que podem ser usadas por invasores para realizar ações maliciosas neste artigo.

Escape do contêiner

Um dos vetores de ataque mais perigosos e comuns contra a infraestrutura de contêineres é o escape do contêiner. Ele consiste na violação dos mecanismos de isolamento do ambiente de contêineres, que possibilita ao invasor interagir com o sistema host.

A possibilidade de escape do contêiner pode surgir por diversos motivos: exploração de vulnerabilidades, erros de configuração de contêineres e uso inseguro da API de conteinerização e orquestração. De fato, o escape do contêiner é o desfecho lógico da maioria dos ataques à infraestrutura de contêineres, já que o principal objetivo do invasor costuma ser a saída do ambiente isolado e a obtenção de acesso ao sistema host ou ao cluster Kubernetes. Assim, o escape do contêiner reúne boa parte dos vetores de ataque analisados neste artigo. Na prática, uma das causas mais comuns de escapes bem-sucedidos do contêiner continua sendo justamente os erros de configuração, que ocorrem com frequência muito maior do que a exploração de vulnerabilidades complexas. Por isso, a seguir, examinamos com mais profundidade os erros de configuração de contêineres e os cenários de ataque relacionados a eles.

Para entender melhor os riscos relacionados a configurações incorretas de contêineres, vamos analisar o conceito de capacidades (capabilities) em sistemas Linux. Trata-se de um mecanismo de concessão granular de privilégios estendidos a processos, que possibilita a execução de ações privilegiadas sem acesso root.

Contêineres privilegiados

Uma das configurações mais perigosas é a execução do contêiner com o parâmetro --privileged. Nesse modo, o contêiner recebe todas as capacidades do Linux, acesso aos dispositivos do host e o privilégio de interagir com interfaces do kernel. Um contêiner assim praticamente deixa de ser um ambiente isolado e, em muitos casos, passa a dispor de capacidades equivalentes ao acesso root no sistema host.

Vamos analisar um exemplo simples de ataque de escape do contêiner com o parâmetro --privileged. Com a ferramenta capsh, é possível constatar que esse contêiner possui praticamente todas as capacidades do Linux. Além disso, o namespace de PID coincide com o do host, já que o processo com PID=1 corresponde ao init (o primeiro processo do sistema Linux); em outra configuração, o primeiro PID corresponderia ao identificador do processo que criou o contêiner. Se uma shell for iniciada a partir do processo init com a ferramenta nsenter, o comportamento esperado é a criação de um processo fora do contêiner, o que pode ser facilmente verificado com o comando hostname.

Configurações incorretas de capacidades do contêiner abrem uma ampla superfície para ataques. A seguir, detalhamos os métodos de escape do contêiner por meio de capacidades específicas.

CAP_SYS_ADMIN

CAP_SYS_ADMIN é considerado uma das capacidades mais perigosas do Linux no contexto da segurança de contêineres. Embora as capacidades do Linux tenham sido concebidos como um mecanismo para fragmentar os privilégios do superusuário em categorias separadas, o CAP_SYS_ADMIN passou, com o tempo, a abranger um número significativo de operações sensíveis do kernel. Dessa forma, um contêiner que possui essa capacidade obtém acesso a diversos mecanismos do sistema que afetam diretamente o isolamento do ambiente de contêineres. Ele passa a poder montar sistemas de arquivos, interagir com o mecanismo cgroups (responsável pela distribuição de recursos), alterar parâmetros do kernel dentro de certos limites, trabalhar com dispositivos loop e empregar diversos mecanismos de gerenciamento de namespaces. Na prática, isso reduz significativamente a fronteira entre o contêiner e o sistema host.

Essa capacidade se torna particularmente perigosa quando combinada com outros erros de configuração. Por exemplo, se o contêiner tiver o parâmetro hostPath configurado, o invasor, após o comprometimento, pode montar diretórios do sistema host dentro do próprio ambiente e obter acesso a arquivos críticos do nó. Da mesma forma, o acesso aos diretórios /proc ou /sys possibilita interagir com mecanismos internos do kernel Linux, o que pode contribuir para ampliar a escala do comprometimento.

Vamos analisar, em um exemplo prático, como a presença de CAP_SYS_ADMIN pode ajudar um invasor a escapar do contêiner. A imagem abaixo ilustra um conjunto de ações dentro de um contêiner que possui a capacidade CAP_SYS_ADMIN e acesso aos diretórios do host. Ao montar um disco do host em uma pasta do contêiner, o invasor passa a poder utilizar livremente todos os arquivos do sistema host. Neste exemplo, é demonstrada a possibilidade de sobrescrever a configuração da shell do superusuário, adicionando nela qualquer payload malicioso.

CAP_SYS_MODULE

CAP_SYS_MODULE concede acesso direto ao mecanismo de carregamento e descarregamento de módulos do kernel. A interação com o espaço do kernel faz do CAP_SYS_MODULE uma capacidade de alto risco, ao contrário de muitas outras capacidades, que permanecem restritas ao espaço do usuário.

Do ponto de vista da arquitetura do Linux, os módulos do kernel são código executado com privilégios máximos no espaço do kernel. Eles podem estender a funcionalidade do sistema, gerenciar dispositivos, a pilha de rede, sistemas de arquivos e outros componentes críticos. Por isso, a possibilidade de carregar dinamicamente esses módulos por meio do CAP_SYS_MODULE equivale à capacidade de influenciar o comportamento de todo o sistema operacional.

Na prática, o CAP_SYS_MODULE raramente é necessário em aplicações modernas de contêineres. A presença dessa capacidade geralmente está associada a arquiteturas legadas, sistemas de monitoramento ou drivers especializados que precisam interagir com o kernel. Por isso, em infraestruturas modernas, o uso do CAP_SYS_MODULE é praticamente proibido. Na maioria dos ambientes, ele é considerado inaceitável, pois seu comprometimento não resulta em uma elevação de privilégios local dentro do contêiner, mas sim na execução de código no espaço do kernel.

O escape do contêiner por meio dessa capacidade ocorre em várias etapas. Nesse caso, o objetivo do ataque é carregar um módulo malicioso do kernel Linux. Vale ressaltar que ele precisa estar em conformidade com a versão do kernel, o que exige que o invasor realize etapas adicionais de reconhecimento no sistema. Esses ataques podem ser realizados totalmente dentro do contêiner, caso ele disponha das ferramentas necessárias para compilar o módulo e tenha acesso aos diretórios com as dependências do kernel. No entanto, na maioria das vezes essas ferramentas não estão presentes na imagem do contêiner, por isso os invasores preparam o payload malicioso com as dependências necessárias em outro host e, posteriormente, o transportam pela rede ou o gravam em um arquivo binário (por exemplo, por meio do echo).

Vamos analisar o escape do contêiner usando um módulo do kernel no exemplo do seguinte payload:

Esse módulo, ao ser carregado, inicializa uma shell reversa. Depois de compilar e entregar o payload no contêiner, o invasor só precisa carregar o módulo no espaço do kernel, após iniciar previamente a escuta da porta no endereço indicado no payload.

CAP_SYS_PTRACE

A capacidade CAP_SYS_PTRACE confere a um processo privilégios estendidos de interação com outros processos do sistema por meio do mecanismo ptrace. Esse mecanismo destina-se à depuração e ao rastreamento de aplicações, mas seu uso incorreto em ambientes de contêineres pode enfraquecer seriamente o isolamento e, em cenários específicos, conduzir ao escape do contêiner e ao comprometimento subsequente do sistema host.

O principal risco do CAP_SYS_PTRACE reside na possibilidade de ler e modificar a memória de outros processos, controlar sua execução, injetar código e acessar dados sensíveis presentes na memória. Além disso, o CAP_SYS_PTRACE permite realizar injeções em processos.

Após comprometer o contêiner, o invasor pode recorrer ao ptrace para se conectar a processos do host. Cabe ressaltar que isso só é possível se os PIDs do host estiverem acessíveis ao contêiner, o que ocorre quando há a configuração hostPID: true. O invasor passa a conseguir injetar código em um processo do host e, por exemplo, abrir uma shell reversa (na maioria dos casos, isso exige um código malicioso adicional). A imagem abaixo ilustra esse ataque, implementado com base em uma PoC disponível publicamente.

CAP_NET_ADMIN

CAP_NET_ADMIN oferece amplos privilégios de gerenciamento da pilha de rede do sistema Linux. Em caso de comprometimento do contêiner, a presença dessa capacidade enfraquece significativamente o isolamento de rede e cria oportunidades adicionais para a continuidade do ataque.

Um contêiner com a capacidade CAP_NET_ADMIN passa a poder alterar parâmetros de interfaces de rede, gerenciar tabelas de roteamento, interagir com mecanismos de filtragem de tráfego e alterar o comportamento da pilha de rede. Embora a maior parte dessas operações permaneça formalmente restrita ao namespace de rede do contêiner, na prática essa capacidade costuma ser combinada com configurações incorretas, como o parâmetro hostNetwork: true, que possibilitam obter acesso a recursos de rede do host.

Depois de obter acesso ao contêiner, o invasor pode usar essa capacidade para modificar seu comportamento de rede e organizar novos ataques dentro da infraestrutura. Um dos cenários mais comuns é a alteração das regras do iptables e o redirecionamento de tráfego, o que possibilita realizar ataques MitM, interceptar o tráfego interno e ocultar a própria atividade maliciosa.

Cabe ressaltar que existem muitas outras capacidades do Linux que possibilitam o escape do contêiner quando combinados com outras configurações incorretas; apresentamos aqui apenas algumas das mais graves e comuns.

Uso da API de orquestração

Um dos vetores de ataque mais perigosos e, ao mesmo tempo, mais recorrentes contra a infraestrutura de contêineres é a exploração de erros de configuração das APIs de gerenciamento de contêineres e dos sistemas de orquestração. Diferentemente dos ataques que demandam a exploração de vulnerabilidades do kernel ou a realização de um escape do contêiner, esse cenário costuma dispensar técnicas complexas: basta que o invasor obtenha acesso às interfaces de gerenciamento do ambiente de contêineres.

O problema reside no fato de que as APIs das plataformas de contêineres detêm, na prática, privilégios de gerenciamento de toda a infraestrutura. A API do Docker, a API do Kubernetes e a API do kubelet possibilitam criar contêineres, alterar configurações, acessar o sistema de arquivos dos nós e executar comandos dentro de contêineres já em execução. Quando configuradas incorretamente, essas interfaces se tornam um ponto de comprometimento de todo o ambiente.

Um dos exemplos mais conhecidos é a API do Docker exposta. Se o daemon Docker estiver acessível via TCP sem TLS ou autenticação, o invasor pode interagir remotamente com o sistema host como um administrador local: iniciar novos contêineres configurados para o ataque, montar o sistema de arquivos do host e executar comandos arbitrários dentro dos contêineres por meio da API. Na prática, o comprometimento da API do Docker costuma resultar na tomada completa do nó em poucas requisições.

Riscos semelhantes manifestam-se também em ambientes Kubernetes. A API do Kubernetes é o ponto central de gerenciamento de todo o cluster. Com um token de ServiceAccount, políticas RBAC fracas ou um servidor de API exposto por engano, o invasor pode executar uma ampla gama de operações.

Como exemplo de ataque, suponhamos que o invasor tenha comprometido um token da API do Kubernetes de uma conta privilegiada. Primeiro, o conjunto de privilégios desse token é determinado, por exemplo, com um script que realiza requisições para cada privilégio individual. Dessa forma, obtém-se a lista completa de privilégios no Kubernetes.

A saída do script evidencia que o token de API obtido tem privilégios extremamente altos no cluster. A próxima etapa lógica do ataque é a criação de um contêiner privilegiado e a exploração de qualquer um dos métodos de escape descritos anteriormente. Em nosso exemplo, a criação do contêiner foi realizada com uma requisição POST à API, com o uso do curl:

Em pod.json, é transmitida a configuração do contêiner necessária para o escape subsequente:

Ao criar um contêiner privilegiado, o invasor passa a ter a possibilidade de realizar o escape do contêiner, com o comprometimento subsequente do sistema host.

Há outros cenários de ataque relacionados a requisições de API. Por exemplo, ao montar o socket do Docker dentro do contêiner, o invasor passa a poder interagir diretamente com o daemon Docker. Após comprometer esse contêiner, o invasor herda, na prática, os privilégios do daemon e, assim, obtém controle sobre todos os contêineres no host.

Para executar esse ataque, os invasores buscam contêineres com sockets montados. O restante do ataque segue o padrão já descrito: é feita uma requisição de API para criar um contêiner privilegiado e, em seguida, também via API, é explorado qualquer um dos métodos de escape.

Ataque à cadeia de suprimentos

Diferentemente dos ataques clássicos, voltados à exploração das vulnerabilidades de um contêiner já implantado, essa abordagem tem como alvo o comprometimento dos componentes antes mesmo deles serem executados no ambiente de execução. A infraestrutura de contêineres moderna está fortemente integrada a um grande número de componentes externos. Dessa maneira, a segurança do contêiner depende diretamente não apenas da própria aplicação, mas de toda a cadeia de build e entrega da imagem. O comprometimento de qualquer uma dessas etapas pode possibilitar que o invasor injete código malicioso em múltiplos contêineres e serviços de uma só vez.

Um dos cenários mais comuns são os ataques por infecção de imagens de contêineres. Em muitas organizações, os desenvolvedores utilizam imagens públicas do Docker Hub ou de outras fontes disponíveis sem realizar uma verificação total de sua origem e conteúdo. Os invasores publicam amplamente imagens infectadas que imitam externamente serviços e ferramentas populares. Depois que esse contêiner é executado dentro da infraestrutura, o invasor passa a poder executar seu próprio código já no interior do ambiente confiável da organização.

Além disso, um dos alvos mais frequentes dos ataques são os sistemas de CI/CD para implantação de contêineres. As plataformas de build e entrega de aplicações costumam dispor de privilégios estendidos. Por exemplo, após obter acesso a um sistema de CI/CD, o invasor pode modificar discretamente as etapas de build da imagem Docker. Em vez de alterar o código-fonte da aplicação, a lógica maliciosa pode ser injetada diretamente no pipeline. Um comando adicional no processo de build pode baixar um binário de terceiros, adicionar um script oculto, alterar a configuração do contêiner ou inserir um mecanismo de controle remoto. Externamente, o contêiner aparentará ser totalmente legítimo, visto que sua funcionalidade principal permanecerá inalterada.

Conclusões

De modo geral, os ataques atuais a ambientes de contêineres evidenciam que a principal ameaça não está apenas dentro do próprio contêiner, mas também na implementação da infraestrutura de contêineres. Os contêineres costumam ser empregados como um ambiente intermediário para o estabelecimento de persistência no sistema: após o comprometimento inicial, os invasores buscam alcançar o nível do sistema operacional host ou obter acesso ao gerenciamento da infraestrutura por meio das APIs de conteinerização e orquestração. Para isso, são exploradas configurações fracas, privilégios excessivos e falhas de isolamento.

Além disso, observa-se uma tendência de deslocamento dos ataques para os pipelines de CI/CD, onde o comprometimento de uma única parte pode levar à tomada de controle de toda a infraestrutura. Por isso, a segurança de ambientes de contêineres hoje abrange a proteção do host, o controle rigoroso de permissões no orquestrador, a minimização de privilégios dos contêineres e a validação de toda a cadeia de suprimentos. Nossa solução Kaspersky Container Security foi concebida considerando as particularidades dos ambientes de contêineres e oferece proteção em diversos níveis, das imagens de contêineres ao sistema host, contribuindo para a materialização do princípio de desenvolvimento seguro de software.

Contêineres em chamas: do escape para o host aos ataques à cadeia de suprimentos

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

Relatórios

O passageiro invisível do seu carro

Um especialista da Kaspersky descobriu um novo malware para Android para exibição de anúncios e criação de uma botnet de proxy. A infecção ocorre por meio de um software legítimo desatualizado para centrais multimídia da DoFun.