- Início
- The Thinking Wire
- Histórico de Build Guarda a Própria Cópia do Seu Token
Histórico de Build Guarda a Própria Cópia do Seu Token
Um scan autônomo começou com um nome de domínio. Sem credenciais, sem código-fonte, sem nenhuma conta dentro do alvo. Cerca de vinte e cinco minutos depois, ele estava com um personal access token do GitHub vivo, com direitos de admin no repositório GitOps que governa os clusters de produção da Baseten.
Esses vinte e cinco minutos são um número instrumentado pelo próprio fornecedor. A Strix, que publicou o relato, constrói o scanner que produziu o achado, e a medição é a instrumentação do próprio produto sobre si mesmo. O cronograma de divulgação, as capturas de tela e a tabela de permissões do post são verificáveis. O relógio não é. Estou carregando o número como reportado e o mecanismo como provado, que são dois níveis diferentes de confiança.
O mecanismo é a parte que vale a sua manhã.
O token nunca esteve em uma camada
Uma imagem de container tem dois tipos de conteúdo. As camadas guardam o sistema de arquivos: arquivos, diretórios, permissões. Um blob de configuração separado guarda metadados sobre como a imagem foi montada, incluindo um array history[], com uma entrada por etapa de build. Cada entrada carrega um campo created_by com o texto do comando daquela etapa.
O token morava em history[].created_by. Não dentro de um arquivo da imagem. No texto registrado do comando que um dia o usou.
O padrão publicado que colocou o token ali:
ARG GITHUB_TOKEN
RUN GITHUB_TOKEN=${GITHUB_TOKEN} bash -c '... git config ... url."https://${GITHUB_TOKEN}@github.com/".insteadOf "git@github.com:" ...'
O segredo chega como argumento de build, é expandido dentro de um comando de shell, e a linha de comando expandida é o que a imagem registra sobre si mesma. Descompactar camadas e varrer o sistema de arquivos com um scanner de segredos não devolve nada, porque nada está em disco para ser encontrado. A credencial está nos metadados, ao lado da tag e do entrypoint, a uma chamada de API de distância de quem conseguir buscar o manifest.
Por isso a varredura de sistema de arquivos foi insuficiente aqui. Ela olhava a metade errada do artefato.
Três anos é muito tempo para um argumento de build
A etapa de build que continha o token rodou em 2023-03-03. O token ainda autenticava em julho de 2026. Mais de três anos de um argumento de build sobrevivendo a todo ciclo de rotação que deveria tê-lo pegado.
O relato cita a Baseten com valuation de US$ 13 bilhões e registra que a empresa já usava ferramentas de segurança com IA. O token sobreviveu de todo modo. A pergunta que isso deixa para o resto de nós é se os nossos scanners leem o blob de configuração, e se tratam uma linha RUN como lugar onde segredo mora.
A porta do registry já estava aberta
O registry gcp-us-east4-zlw.registry.baseten.co tinha um projeto público. Listagem anônima funcionava. Tokens anônimos de pull eram emitidos. Manifests e blobs eram baixáveis. Quem consegue puxar um manifest consegue puxar o blob de configuração, e é no blob de configuração que vive o array history[].
Um detalhe da execução merece atenção independente do resultado. Um par de credenciais encontrado antes, chaves AWS, estava morto: sts:GetCallerIdentity respondeu InvalidClientTokenId. A execução seguiu adiante. Um pipeline que para na primeira string interessante teria arquivado um relatório de chave morta e encerrado o caso.
O que a credencial realmente abria
O GitHub respondeu com X-OAuth-Scopes: repo para a conta basetenbot na organização basetenlabs. Dali, a tabela de permissões mostrou admin e push em três repositórios, descritos em prosa como o repositório principal do produto, o repositório GitOps que governa os clusters e o Homebrew tap da empresa. Leitura e escrita em outros quatro repositórios privados, incluindo repositórios por cliente.
Os nomes dos repositórios estão redigidos na página de origem como basetenlabs/b***. Aqui eles seguem redigidos.
Admin em um repositório GitOps é a linha que deveria fazer um engenheiro de plataforma parar de ler e ir verificar alguma coisa. O repositório é o estado desejado. Quem escreve nele muda aquilo para o que os clusters convergem, e a mudança entra pelo mesmo pipeline que as mudanças legítimas usam.
Tornar o registry privado não fechou o buraco
A divulgação correu assim. O relatório chegou em 13 de julho às 23:10. Na manhã de 14 de julho, o projeto Harbor foi tornado privado. O token continuava vivo. Às 16:34 de 14 de julho, o achado foi confirmado como crítico e o token foi rotacionado. O caso fechou em 17 de julho. O bounty foi pago em camisetas e blusas de moletom.
Entre a manhã e a tarde de 14 de julho está a lição. Tornar o registry privado fechou o canal de distribuição. Não fez nada com a credencial, porque toda cópia daquela imagem já puxada continuava carregando a mesma string no blob de configuração, e a credencial em si seguia inalterada. Fechar a porta depois que as cópias saíram é arrumação. Rotação é remediação. A ordem importa, e o incidente mostra o custo de invertê-la.
A Baseten confirmou e fechou o caso em uma divulgação coordenada, com o rascunho compartilhado de antemão. Esse é o comportamento que se espera de quem recebe o relatório, e é por isso que o caso vale como mecanismo a estudar. A empresa aqui é circunstância; o padrão de build é o assunto.
O agente parou na prova
Há uma segunda coisa nessa execução à qual eu volto sempre, e ela não tem relação com Docker.
O agente encontrou uma credencial de admin funcional no repositório que controla produção e então não fez nada com ela. Chamadas somente de leitura para estabelecer o escopo. Parada na prova. Sem clone, sem push, sem mudança de configuração. O relato é explícito: o acesso foi demonstrado e não foi exercido.
Coloque isso ao lado do modo de falha que de fato já nos morde: o agente que reporta uma correção que nunca rodou, ou uma reprodução montada com texto plausível. Já argumentamos que segurança de agentes é um problema de arquitetura e que uma identidade não humana precisa do isolamento que se daria a um insider. Esta execução é um dado do outro lado desse argumento. Um agente ofensivo, operando no momento mais tentador de toda a sua corrida, com admin sobre um plano de controle de produção, respeitou uma fronteira estreita o bastante para ser auditada depois.
Minha leitura é que uma fronteira se sustenta quando é escrita como condição de parada, e não como preferência. Provar o acesso, registrar a evidência, encerrar. A forma de um modelo de autorização que um agente consegue obedecer é essa: uma lista de verbos que ele pode emitir e um ponto em que a tarefa acabou.
Faça isso agora
Duas verificações, nesta ordem, antes do almoço.
Puxe o blob de configuração de uma imagem que seu pipeline de build publicou no último ano e leia o histórico de build dela. docker history com a saída completa, ou a leitura do JSON de configuração direto do seu registry, mostram o created_by de cada etapa. Você procura qualquer linha RUN contendo um token, uma URL com credencial embutida, ou um git config que embuta uma. Faça isso em uma imagem cujo Dockerfile use ARG para algo sensível, porque essa é a forma que produziu o achado.
Depois, pegue qualquer token de longa duração do GitHub que sua automação carrega e pergunte ao GitHub o que ele pode fazer. O header de resposta X-OAuth-Scopes informa o escopo, e a listagem de repositórios informa o raio de alcance. Se uma conta de bot tem admin no repositório de onde vem o estado do seu cluster, esse é o achado, e fechá-lo é seu, sem depender de alguém de fora reportar.
Se a primeira verificação encontrar um token, rotacione antes de mexer em controle de acesso no registry. Cópias de uma imagem não voltam por convocação. Uma credencial rotacionada morre em todo lugar de uma vez.
E já que você está aí dentro, anote quais metades do artefato seus scanners de fato leem. Essa lista fica ao lado do inventário de materiais de IA, porque um componente catalogado e um componente efetivamente varrido não são a mesma afirmação.
Fontes
- Strix. “We wanted to use Baseten for inference. We ended up with admin access to Baseten GitHub repos.” Setembro de 2026.
A Victorino ajuda times de engenharia a auditar o que seus agentes e pipelines de build alcançam, e a definir fronteiras de autorização que um agente consiga de fato respeitar: contato@victorino.com.br | www.victorino.com.br
Todos os artigos do The Thinking Wire são escritos com o auxílio do modelo LLM Opus da Anthropic. Cada publicação passa por pesquisa multi-agente para verificar fatos e identificar contradições, seguida de revisão e aprovação humana antes da publicação. Se você encontrar alguma informação imprecisa ou deseja entrar em contato com o editorial, escreva para editorial@victorino.com.br . Sobre o The Thinking Wire →
Se isso faz sentido, vamos conversar
Ajudamos empresas a implementar IA sem perder o controle.
Agendar uma Conversa