16 - Hunting em Containers e Kubernetes
video: https://youtu.be/... no frontmatter da nota.Hunting em Containers e Kubernetes
Nota 16 do capítulo 08 (Técnicas de Hunting por TTPs), levando as técnicas de caça aos ambientes orquestrados e efêmeros.
Mapeia a telemetria disponível (audit logs do Kubernetes, Falco, runtime de node), as TTPs de containers e as consultas por hipótese.
Dialoga com as notas 12 a 15, com o capítulo 26 (ambientes modernos) e com os controles PRT.4, TH.11 e DAT.4 do capítulo 15.
1. Contexto e Posicionamento na Biblioteca
Ambientes orquestrados mudam a economia da caça: pods vivem minutos, nós são gado e imagens são reconstruídas a cada deploy, então forense de disco quase nunca existe no momento da pergunta. A evidência migra para dois planos, o plano de controle, onde o API server registra toda intenção antes da execução, e o plano de runtime, onde syscalls e eventos de container registram a execução real. O ATT&CK formalizou esse domínio com a série T16xx de técnicas de containers, e as plataformas geridas, EKS, GKE e AKS, exportam o audit log do plano de controle para o SIEM como qualquer outra fonte.
O posicionamento na biblioteca segue dois eixos. Com o capítulo 26, esta nota compartilha o tema de ambientes modernos, mas aqui o foco é o método de caça e não a arquitetura. Com o capítulo 15, o PRT.4 define os controles de proteção que reduzem a superfície, e esta nota assume que parte deles pode falhar, porque é aí que a caça entra. A mudança conceitual mais importante para o caçador vindo de endpoint é a identidade: no cluster, o adversário costuma ser uma service account, não uma pessoa, e as hipóteses precisam ler o API server com essa gramática.
2. Telemetria Disponível e Onde Coletar
A fonte central é o audit log do Kubernetes, habilitado no API server por audit policy, que registra cada chamada com verbo, usuário, origem, objeto e resposta. O desenho da policy define o custo e o valor: leituras com nível Metadata, escritas com Request, e subrecursos sensíveis como exec e portforward com RequestResponse, que capturam o corpo da requisição. Os stages RequestReceived e ResponseComplete cobrem o essencial sem duplicar volume. Sem export imediato para fora do cluster, o log vira alvo do próprio incidente, então o DAT.4 do capítulo 15 é pré-requisito, não opção. A tabela abaixo resume as fontes e os campos de caça.
| Fonte | O que registra | Campos de maior valor |
|---|---|---|
| K8s audit logs do API server | Toda chamada ao plano de controle | verb, user.username, sourceIPs, objectRef (resource, subresource, namespace, name), requestObject, responseStatus.code, stage |
| Plano de controle da nuvem (EKS, GKE, AKS) | Operações de cluster e de node | Ator, ação, região, recursos modificados |
| Falco e runtime security | Syscalls e eventos de runtime com contexto de container | container.id, container.name, image, proc.cmdline, evt.type, user |
| Runtime de container (containerd, CRI-O) | Ciclo de vida de pods e imagens | Criação, start, stop, pulls de imagem, OOM |
| Node (journald, kernel, SSH) | Atividade que escapa do namespace | sudo, módulos de kernel, sessões SSH, alterações em /etc |
| Metadata e IAM da nuvem | Uso de credenciais de nó e de conta de serviço | Chamadas ao metadata, AssumeRole incomum |
O Falco roda como daemonset com driver eBPF ou módulo de kernel e converte syscalls em eventos com contexto de container, pod e imagem, com saída para o SIEM. As regras do Falco seguem a disciplina de código do capítulo 18: repositório versionado, desc com justificativa, tags consistentes e revisão de falsos positivos por ciclo. O runtime de container complementa com pulls de imagem fora do registry aprovado, que é sinal direto de T1612, build de imagem no próprio host, e de imagem trojaneada em cadeia de supply chain.
3. TTPs de Containers e Kubernetes
As técnicas da série T16xx têm manifestação observável precisa, e a tabela abaixo concentra as oito que geram caça recorrente. A combinação pod privilegiado com hostPath é o prelúdio padrão do escape para o host; o abuso de token de service account é o passo lateral mais comum em incidentes reais; e o cryptomining é a monetização típica de clusters expostos.
| TTP | Manifestação típica | ATT&CK |
|---|---|---|
| kubectl exec não autorizado | Shell ou comando dentro de pod de produção por conta fora da janela | T1609 |
| Pod privilegiado ou com hostPath | securityContext privileged true ou volume hostPath na raiz | T1610 |
| Escape para o host | Mounts sensíveis, exploit de kernel, cgroup release_agent | T1611 |
| Abuso de service account token | Token montado usado do pod para listar secrets e pods | T1552.007 e T1528 |
| Descoberta do ambiente | list e get em pods, secrets e api-resources via API | T1613 |
| Build de imagem no host | docker build com payload direto no node | T1612 |
| Cryptomining | CPU sustentada, conexão a pool, deployment com réplicas altas | T1496 |
| Uso direto do kubelet API | Requisições à porta 10250 contornando o API server | T1609 |
Casos públicos dão escala ao repertório. O CVE-2018-1002105 demonstrou que o próprio API server pode virar proxy de abuso quando o bug permite escalada; kubelets com 10250 aberto e autenticação fraca foram fonte de sequestro de container por anos; e campanhas de cryptomining em clusters mal configurados mostram o ciclo completo, exposição, deployment, mineração e persistência por DaemonSet. O aprendizado para a caça é que o plano de controle entrega a intenção e o runtime entrega o ato, e hipóteses fortes cruzam os dois no mesmo minuto.
4. Hipóteses e Consultas de Exemplo
A tabela abaixo amarra as hipóteses da rotina às fontes e aos sinais, no formato SE e ENTÃO do capítulo 07. Cada linha vira item do catálogo com dono e cadência.
| Hipótese | Fonte | Sinal observável |
|---|---|---|
| Exec em produção por conta humana fora da janela de manutenção | Audit log | verb create em pods/exec com usuário fora da allowlist |
| Pod privilegiado criado recentemente | Audit log | requestObject com privileged true ou hostPath montado |
| Token de service account usado para descoberta | Audit log | verbs list e get em secrets e pods por SA de workload |
| Shell em container de produção | Falco | spawned_process de shell dentro de container |
| Mineração em andamento | Falco e métricas | Processo de minerador, conexão a pool, CPU sustentada |
O jq abaixo executa a primeira hipótese sobre o audit log exportado, devolvendo usuário, pod, namespace e IP de origem de cada exec. A allowlist entra no próximo estágio do pipeline, porque o hunt iterativo prefere ver tudo e filtrar depois.
jq -c 'select(.verb=="create" and .objectRef.resource=="pods" and .objectRef.subresource=="exec")
| {ts: .stageTimestamp, usuario: .user.username, pod: .objectRef.name,
ns: .objectRef.namespace, ip: .sourceIPs[0]}' k8s-audit.json | head -30
A segunda consulta encontra criação de pod privilegiado ou com hostPath, o prelúdio de escape que merece fila própria de triagem.
jq -c 'select(.verb=="create" and .objectRef.resource=="pods")
| select((.requestObject.spec.containers[]?.securityContext.privileged == true)
or (any(.requestObject.spec.volumes[]?; .hostPath != null)))
| {ts: .stageTimestamp, usuario: .user.username, ns: .objectRef.namespace,
pod: .objectRef.name}' k8s-audit.json
A terceira consulta mede descoberta por service account, ranqueando qual SA lista secrets e pods e em qual namespace, baseline direto para o caso da seção 5.
jq -r 'select(.user.username | startswith("system:serviceaccount:"))
| select(.verb=="list" or .verb=="get")
| select(.objectRef.resource=="secrets" or .objectRef.resource=="pods")
| [.user.username, .verb, .objectRef.resource, .objectRef.namespace] | @tsv' k8s-audit.json |
sort | uniq -c | sort -rn | head -25
Em ambiente Azure, a mesma hipótese de exec roda sobre a tabela AKSAudit do Sentinel, com o JSON do evento parseado em colunas, como abaixo, lembrando que o conector oferece também a variante AKSAuditAdmin com campos sensíveis.
AKSAudit
| where TimeGenerated > ago(7d)
| extend Evento = parse_json(Log)
| extend Verb = tostring(Evento.verb), Usuario = tostring(Evento.user.username),
Recurso = tostring(Evento.objectRef.resource), Sub = tostring(Evento.objectRef.subresource),
Pod = tostring(Evento.objectRef.name)
| where Verb == "create" and Recurso == "pods" and Sub == "exec"
| summarize Execs = count() by Usuario, Pod
| order by Execs desc
A regra Falco abaixo fecha a hipótese de shell no runtime, com exclusão versionada para as imagens de diagnóstico aprovadas, e o padrão de output segue o formato que alimenta o SIEM.
- rule: Shell em Container
desc: Shell interativo iniciado dentro de container de producao
condition: >
spawned_process and container and
proc.name in (bash, sh, dash, zsh, ash) and
not container.image.repository startswith "registry.interno/diagnostico"
output: shell em container (usuario=%user.name pod=%k8s.pod.name imagem=%container.image.repository comando=%proc.cmdline pai=%proc.pname)
priority: WARNING
tags: [container, shell, runtime]
5. Caso de Caça: Exec Não Autorizado
O caso da biblioteca parte do baseline de quem pode executar comandos em produção: a service account ci-deploy-bot dos runners de CI, dois administradores humanos e o break-glass documentado. O primeiro passo roda a consulta de exec de 30 dias e confirma que prod/pagamentos não registra exec humano; o segundo passo, na execução da caça, encontra system:serviceaccount:default:build-bot executando comando no pod api-pag-7f9d às 02h14, com sourceIP 203.0.113.9, ASN externo de contratada, enquanto build-bot historicamente só age no CIDR de CI 10.90.0.0/24. A anomalia dupla de identidade e origem já sustenta prioridade alta.
O terceiro passo cruza o runtime no mesmo minuto: Falco registra bash no mesmo pod, seguido de curl ao metadata 169.254.169.254 com resposta listando role do node, e de leitura de /proc/1/environ, padrão de coleta de segredos em variáveis de ambiente. O audit log completa o quadro às 02h19: list de secrets no namespace pagamentos usando o token da própria build-bot, montado no pod porque o chart de deploy habilita automountServiceAccountToken por padrão. O root cause é de configuração: a SA de CI estava vinculada a cluster-admin por conveniência do chart, e o token exposto valeu como identidade total.
A resposta e a conversão fecham o caso. Conteção revogou sessões e token, rotacionou os secrets do namespace e suspendeu a integração da contratada até revisão; erradicação corrigiu RBAC para permissões mínimas, desligou automount onde desnecessário e limitou egress para o metadata com política de rede e hop limit no IMDS. A conversão em detecção criou duas regras permanentes: consulta agendada no SIEM para exec por identidade fora da allowlist, e regra Falco para leitura de environ seguida de acesso ao metadata, ambas no repositório do capítulo 18. O registro do caso anotou a lacuna: até o hunt, nenhuma detecção cobria exec anômalo, o que entra como métrica de cobertura no capítulo 11.
6. Erros Comuns e Pontos de Atenção
- Caçar sem audit policy desenhada; efeito: volume incontrolável ou lacuna total de escritas; correção: Metadata para leituras, Request para escritas e RequestResponse para exec e portforward.
- Confundir identidade humana com identidade de workload; efeito: a caça não acha nada porque o adversário age como service account; correção: construir baseline separado por tipo de user.username.
- Ignorar automount de tokens; efeito: todo pod vira ladrão em potencial da identidade da SA; correção: desligar por padrão, habilitar por exceção e caçar uso anômalo do token.
- Analisar o node por evento isolado; efeito: escape de container parece processo comum no syslog; correção: correlacionar syscall com contexto de container e com o audit do mesmo minuto.
- Depender apenas de scanner de imagem; efeito: build feito no host e imagem trojaneada na cadeia ficam invisíveis; correção: caçar T1612 e pulls fora do registry aprovado.
- Excluir identidades de sistema do hunt; efeito: adversário que opera com identidade de nó ou de kube-proxy some do radar; correção: criar baseline também para system:nodes e cia, com exceções versionadas.
- Manter o audit log apenas no cluster; efeito: atacante com privilégio apaga a própria trilha; correção: export imediato para SIEM fora do cluster, conforme o DAT.4.
7. Integração com o Restante da Biblioteca
O PRT.4 do capítulo 15 define os controles de proteção de containers que reduzem a superfície, e esta nota cobre a camada de caça que assume falha de controle; os dois juntos fecham o ciclo defesa e verificação. O TH.11 dá continuidade quando o achado pula para o node, tratado como endpoint tradicional, e o DAT.4 garante que o audit log sobreviva ao incidente. Com o capítulo 26, a fronteira é de método: lá está a arquitetura de ambientes modernos, aqui está o catálogo de hipóteses. A conversão das caças em regras Falco e consultas agendadas pertence ao capítulo 18, e a cobertura da série T16xx alimenta o mapa de lacunas do grupo MAP do capítulo 15.
A nota encerra com um princípio de priorização que organiza o backlog de caça em cluster: o API server é o sensor ideal porque vê intenção antes da execução, então caçadas que movem a detecção para o plano de controle, mudança de RBAC, criação de pod com spec suspeito, uso de SA fora do padrão, custam menos e acionam mais cedo que caçadas apenas de runtime. Runtime permanece indispensável porque prova o ato, mas o volume do backlog deve ser preenchido primeiro pelo plano de controle, e o runtime entra onde a intenção do API server não alcança, escape de container e abuso direto de kubelet.