Um Guia para Vulnerabilidades de Escalação de Privilégios em Container
Uma das muitas promessas dos Container é o isolamento.
Através do uso cuidadoso de namespaces Linux e cgroups, os Container criam um sandbox onde você pode executar processos independentemente uns dos outros. Isso, combinado com uma experiência de desenvolvedor agradável, fez com que os Container ganhassem popularidade entre engenheiros e profissionais de segurança.
Mas isso é realmente suficiente?
E você pode afirmar com certeza que suas cargas de trabalho estão seguras caso uma delas seja comprometida?
Bem, neste post, abordaremos o sonho de todo atacante e o pesadelo dos sysadmins, explicando como e por que isso acontece, e as formas de preveni-lo antes que você acabe na primeira página do Hacker News. Para mais vulnerabilidades de segurança de contêineres Docker, confira: As 9 Principais Vulnerabilidades de Segurança de Contêineres Docker.
Isolamento de Container e Suas Lacunas de Segurança
Um detalhe crucial frequentemente negligenciado com contêineres é que todos eles compartilham o mesmo host. Isso significa que cada Container é tão seguro quanto o próximo. Se o host ou um único Container for comprometido, isso pode significar um desastre para sua infraestrutura.
Isso não significa que você deva descartar os contêineres completamente. Não. O ponto aqui é entender as entrelinhas.
O isolamento começa a falhar quando atacantes encontram uma forma de escalar privilégios. A escalada de privilégios é uma das jogadas mais emocionantes, porém desafiadoras, em ataques de segurança ofensivos. Ela transforma um ponto de apoio com baixos privilégios em um comprometimento total do sistema.
Vulnerabilidades de escalada de privilégios de Container tornam o contorno das barreiras de segurança menos desafiador e muito mais recompensador, pois um único Container é tudo o que é preciso para obter acesso a um host.
Isso não é apenas teoria; a infame vulnerabilidade do runC de 2019 (CVE-2019-5736) permitiu que atacantes sobrescrevessem o binário runC do host e assumissem o controle root do host.
Esses ataques estão longe de serem superficiais, e é por isso que você pode se deparar com perguntas como esta:

Mais recentemente, vulnerabilidades como CVE-2024-21626 e ataques sutis baseados em camadas nos mostraram que mesmo contêineres não-root podem se tornar perigosos quando você usa indevidamente as capacidades do Linux, montagens ou camadas do sistema de arquivos.
É por isso que, com base em nosso guia anterior sobre vulnerabilidades do Docker, exploraremos como esses ataques funcionam, os CVEs recentes que destacam seus riscos crescentes e como preveni-los.
Vulnerabilidades Recentes de Escalada de Privilégios de Container
Embora nem todas as vulnerabilidades de contêineres levem à escalada de privilégios, as mais perigosas frequentemente o fazem. Algumas vulnerabilidades contornam o isolamento de Container e assumem privilégios elevados como nenhum outro.
A seguir, três vulnerabilidades recentes de escalada de privilégios de Container.
CVE-2024-21626: Breakout do Diretório de Trabalho do runC
CVE-2024-21626 é uma vulnerabilidade de Escape de Container de alta gravidade que afeta o runtime de Container amplamente utilizado, runC. A vulnerabilidade surge de um uso inseguro da chamada prctl PR_SET_NO_NEW_PRIVS com execve, permitindo que um atacante contorne a flag no_new_privs quando o Container foi iniciado com montagens ou capacidades excessivamente permissivas.
A flag no_new_privs é uma das principais mitigações contra a escalada de privilégios em que muitos engines de Container confiam para sandboxing.
A falha foi introduzida em versões do runC a partir da v1.1.11 e foi corrigida na v1.1.12.
CVE-2023-2640 e CVE-2023-32629 no Módulo OverlayFS do Kernel do Ubuntu
OverlayFS é um dos blocos de construção fundamentais de contêineres e Kubernetes. Ele sobrepõe dois diretórios (lowerdir e upperdir) em um único host Linux e os apresenta como um único diretório, proporcionando melhor desempenho para comandos Docker relacionados a camadas, como docker build e docker commit.
CVE-2023-2640 e CVE-2023-32629 permitem que um ator malicioso use um Container privilegiado não-root com montagem de volume para escalar privilégios. Isso é possível porque as montagens de volume são tratadas como discos separados e podem ser usadas para criar o OverlayFS, que existe fora da camada do Container. Isso permite que atacantes contornem as restrições padrão do Container e manipulem atributos de arquivo estendidos em volumes montados, que são então copiados para a camada superior com capacidades elevadas (como CAP_SETUID ou CAP_SYS_ADMIN) intactas.
CVE-2022-0492
Em 4 de março de 2022, pesquisadores de segurança descobriram uma falha crítica no cgroup_release_agent_write do kernel Linux que tinha o potencial de permitir o Escape de Container e assumir o controle de todo o nó no qual o Container é executado.
Provedores de Cloud e distribuições Linux agiram rapidamente. Patches foram emitidos, avisos foram publicados. Mas por baixo da superfície, a complexidade permaneceu. Ao contrário de outros softwares, o kernel Linux carece de um esquema de versionamento padronizado entre as distribuições.
O que significa que a aplicação desses patches e a atualização das imagens base levaram meses. Durante esse período, os ambientes permaneceram suscetíveis a ataques potenciais.
Prevenindo Escaladas de Privilégios no Docker e Docker Compose
A melhor forma de prevenir ataques de escalada de privilégios de dentro de um Container Docker é configurar as aplicações do seu Container para rodarem como usuários não privilegiados e, então, garantir que os contêineres não tenham acesso privilegiado.
Na prática, para prevenir a escalada de privilégios no Docker, você precisa fazer o seguinte:
- Bloquear escalonamento de privilégios
- Defina um usuário não-root adequado
- Remova as capacidades do kernel Linux
Você tem duas opções: em tempo de execução ou dentro da sua configuração do Docker Compose.
Em tempo de execução, use as seguintes flags:
docker run \
--read-only \
--security-opt=no-new-privileges \
--network your-isolated-network \
--cap-drop ALL
--cap-add CHOWN \
--pids-limit 99 \
--user=your-user \ # seu usuário não-root.
... # OUTRAS OPÇÕES AQUI
your-app:v1.0.1Com o Docker Compose, a configuração será:
services:
webapp:
image: your-app:v1.0.1
read_only: true
security_opt:
- no-new-privileges:true
networks:
- your-isolated-network
cap_drop:
- ALL
cap_add:
- CHOWN
# OUTRAS OPÇÕES AQUI
...Executando Contêineres que Exigem Acesso de Usuário root
Existem casos (execução de utilitários de sistema, serviços legados, etc.) em que um contêiner pode exigir acesso root para ser executado. Nesses casos, você pode remapear este usuário para um usuário com menos privilégios no host Docker.
O remapeamento de um UID de contêiner garante que, mesmo que o contêiner pense que está sendo executado como root, ele na verdade opera como um usuário sem privilégios da perspectiva do host, o que reduz o risco de fuga de contêiner e comprometimento do host.
Você pode consultar a documentação do Docker sobre remapeamento de namespace de usuário para etapas detalhadas e melhores práticas.
Prevenindo o Escalonamento de Privilégios no Kubernetes
No Kubernetes, a melhor forma de prevenir o escalonamento de privilégios é durante a criação do contêiner, utilizando a configuração spec.containers.securityContext. Isso ocorre porque, uma vez que você cria um contêiner com privilégios excessivamente permissivos, ele não pode ser atualizado em tempo de execução, e você terá que excluí-lo e recriá-lo.
O securityContext a seguir protege o contêiner do aplicativo contra escalonamento de privilégios, mesmo com versões Linux do CVE-2022-0492 não corrigido, conforme mencionado acima.
containers:
- name: app
image: myapp:bullseye-20230912
securityContext:
runAsUser: 1000 # Usar um ID de usuário não-root
runAsGroup: 1000 # Usar um ID de grupo não-root
runAsNonRoot: true # Forçar explicitamente a execução não-root
allowPrivilegeEscalation: false # Impedir que o processo obtenha mais privilégios
readOnlyRootFilesystem: true # Impedir gravações no sistema de arquivos root
capabilities:
drop:
- ALL # Remover todas as capabilities para minimizar a superfície de ataque
add:
- NET_BIND_SERVICE # Adicionar de volta apenas as capabilities que a aplicação precisa
seccompProfile:
type: RuntimeDefault # Usar o perfil seccomp padrão do runtime do contêinerOutra ótima maneira de prevenir o escalonamento de privilégios no Kubernetes é impedir que Pods sem um security context sejam agendados. Os controladores de admissão do Kubernetes permitem interceptar requisições ao servidor API, por exemplo, um deployment, e validar se certas condições são atendidas antes de serem processadas.
Para casos de uso mais avançados, você pode escrever um controlador de admissão do zero; no entanto, soluções de código aberto como Kyverno permitem escrever políticas de cluster que podem corrigir deployments que não possuem um security context definido. Isso geralmente se parece com o seguinte:
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: add-default-securitycontext
annotations:
policies.kyverno.io/title: Adicionar securityContext Padrão
policies.kyverno.io/category: Exemplo
policies.kyverno.io/subject: Pod
policies.kyverno.io/minversion: 1.6.0
policies.kyverno.io/description: >-
Uma entrada securityContext de Pod define campos como o usuário e o grupo que devem ser usados para executar o Pod.
Às vezes, escolher valores padrão para usuários, em vez de bloquear, é uma alternativa melhor para não impedir tais definições de Pod. Esta política irá mutar um Pod para definir os campos `runAsNonRoot`, `runAsUser`, `runAsGroup` e `fsGroup` dentro do securityContext do Pod, caso ainda não estejam definidos.
spec:
rules:
- name: add-default-securitycontext
match:
any:
- resources:
kinds:
- Pod
mutate:
patchStrategicMerge:
spec:
securityContext:
+(runAsNonRoot): true
+(runAsUser): 1000
+(runAsGroup): 3000
+(fsGroup): 2000O manifesto acima usa a definição de recurso personalizado (CRD) ClusterPolicy para aplicar patches em pods via mutate.PatchStrategicMerge e um security context com um usuário não-root.
Contêineres Seguros São Construídos, Não Assumidos
Embora os contêineres forneçam isolamento e uma maneira confiável de construir e executar aplicações, a natureza em constante mudança da segurança de contêineres significa que você não pode depender apenas de medidas preventivas. Em vez disso, você deve usar uma estratégia que permita responder rapidamente aos incidentes à medida que ocorrem.
Essa estratégia começa com varredura proativa.
Com o Aikido, você pode escanear automaticamente imagens de contêiner para descobrir pacotes vulneráveis, runtimes desatualizados ou instruções Dockerfile arriscadas antes que cheguem à produção. Seu scanner de dependências de código aberto estende essa proteção verificando suas bibliotecas em busca de CVEs exploráveis e até mesmo vulnerabilidades corrigidas silenciosamente que os atacantes podem usar para obter privilégios escalonados.
Essas varreduras são reforçadas por verificações de IaC e configuração que sinalizam configurações inseguras do Kubernetes ou Docker, e por portões de segurança CI/CD que impõem políticas de menor privilégio no nível do pipeline. E como a prevenção nunca pode ser perfeita, o Aikido Zen adiciona proteção em tempo de execução para detectar e responder a comportamentos anormais de contêineres em tempo real.

