O token roubado não precisava de servidor
A Wiz Research rastreou o Gheist, uma campanha automatizada que pega tokens do GitHub em logs de infostealers e em vazamentos antigos da cadeia de suprimentos e planta um workflow que despeja segredos em cada repositório que cada token alcança: mais de 13.500 branches de mais de 850 contas. Os segredos saem pela própria API do GitHub, então não há servidor do atacante para bloquear. No Bromure Agentic Coding, o token do GitHub que um stealer encontra na máquina de um desenvolvedor é um substituto que o GitHub rejeita em qualquer outro lugar, e toda escrita feita de dentro da máquina espera por uma caixa de diálogo no seu Mac.
O Gheist não tem servidor de comando. O GitHub executa o payload, o GitHub grava os segredos em um log, e o GitHub entrega o log a quem tiver o token.
Em 25 de outubro, a Wiz Research publicou sua análise do Gheist, uma campanha que ela acompanha desde 15 de setembro. Uma ferramenta automatizada pega um token roubado do GitHub, lista todos os repositórios que esse token alcança e planta um workflow que imprime os segredos do repositório. A Wiz recuperou mais de 13.500 branches plantadas a partir de mais de 850 contas comprometidas e, em 24 de outubro, a campanha ainda estava ativa e ainda mudando. O The Hacker News noticiou o caso ontem.
A ferramenta só precisa de uma entrada, e a Wiz diz de onde ela vem: os tokens correspondem a logs de infostealers e a tokens expostos em comprometimentos anteriores da cadeia de suprimentos, como o do scanner Trivy em março. A orientação da Wiz a cada vítima inclui esta linha: verifique se há infostealers nos dispositivos dos donos dos tokens.
Como o Gheist funciona
Para cada repositório, a ferramenta cria uma branch chamada ci-sync- seguida
de seis caracteres aleatórios e faz commit de um único arquivo,
.github/workflows/sync-check.yml, com a mensagem ci: sync config. O commit
não é assinado e aparece em nome do dono do token. Suas requisições de API
usam o user agent gheist, que deu nome à campanha.
O push dispara o workflow. O job imprime suas variáveis de ambiente e todo o
contexto secrets no log da execução, ambos codificados em base32. A Wiz
explica por que isso funciona: "O GitHub mascara valores secretos nos logs
apenas quando eles aparecem literalmente." A saída em base32 não corresponde
aos valores armazenados, então nada é mascarado. Uma matriz sobre os ambientes
de implantação do repositório puxa os segredos de escopo de ambiente, que
muitas vezes são os de produção.
Depois, a ferramenta baixa o log pela API do GitHub e apaga a execução segundos depois. Nas palavras da Wiz: "Como os segredos saem pela própria API do GitHub, não há domínio do atacante nem conexão de saída para bloquear."
A ferramenta continuou melhorando. Uma variante adicionou um timeout de seis minutos, a matriz de ambientes e mudanças de payload por repositório poucas horas depois da primeira execução, em 28 de setembro. Em 19 de outubro, ela ganhou um job voltado a runners auto-hospedados que caça 31 tipos de token, credenciais de instâncias de nuvem e o token da conta de serviço do Kubernetes.
Onde a nova regra do GitHub para
Desde 28 de julho, o GitHub retém execuções de workflow que classifica como
maliciosas em repositórios públicos até que um colaborador as aprove. Todas as
execuções públicas do Gheist que a Wiz verificou estavam paradas em
action_required. "O controle funcionou, e os payloads maliciosos nunca
rodaram."
A retenção só vale para repositórios públicos no github.com. Em repositórios
privados e no GitHub Enterprise Server, a Wiz encontrou logs de auditoria
mostrando execuções ci-sync-* que terminaram e depois foram apagadas, além de
registros do CloudTrail de workflows de organizações assumindo papéis da AWS a
partir dessas branches.
O token vem da máquina de um desenvolvedor
Cada branch do Gheist precisa de um token válido, e a Wiz rastreou esses tokens
até logs de stealers e ferramentas de build vazadas. No notebook de um
desenvolvedor, o login da CLI gh, um token de acesso pessoal em
~/.git-credentials, um GH_TOKEN em um perfil de shell: um infostealer copia
tudo isso de uma vez. Os agentes de código tornaram esses tokens mais comuns,
porque um agente que envia branches e abre pull requests precisa ter um ao
alcance o tempo todo.
O roubo pode vir de uma ferramenta em que você confia. Em 19 de outubro, um atacante que controlava havia meses a conta do GitHub de um desenvolvedor da OVHcloud trocou os binários de cinco versões da CLI da OVHcloud, da v0.11.0 à v0.15.0, por builds que, nas palavras do CEO da OVHcloud, Octave Klaba, "recuperam segredos do disco". O BleepingComputer informou que as builds para macOS, Linux e Windows foram todas afetadas. O conselho de Klaba: "Se você foi afetado, troque todos os segredos que estão no seu disco."
Um token roubado não precisa chegar a uma campanha como o Gheist no mesmo dia. Logs de stealers são negociados, e um token continua útil até que alguém o revogue.
O mesmo token em uma máquina Bromure
O Bromure Agentic Coding roda Claude Code, Codex, Grok, Kimi e omp em VMs Ubuntu no seu Mac. As credenciais reais ficam no host, e o agente trabalha com substitutos. Siga um token do GitHub pela cadeia do Gheist em uma dessas máquinas.
O token no disco é um substituto
Adicione seu token do GitHub à máquina em Credentials. Na inicialização, o
Bromure grava um falso na VM: ghp_ seguido de 36 caracteres, o formato que o
gh espera, em GH_TOKEN, na configuração do gh e em ~/.git-credentials.
O git push e o gh pr create do agente funcionam como antes. Quando uma
requisição sai da VM para o github.com, o proxy no Mac troca o falso pelo token
real, e só para esse host.
Agora execute a CLI adulterada dentro dessa VM, como um agente faria depois de
seguir as instruções de instalação de um projeto. Ela lê o disco, e todo valor
do GitHub que encontra é o substituto. O mesmo vale para o resto do butim
habitual de um desenvolvedor. O ~/.aws/config aponta para um auxiliar que
entrega uma chave secreta falsa, e o proxy assina de novo as requisições da AWS
com a verdadeira, no Mac. A VM não tem chaves privadas SSH para ler: o
SSH_AUTH_SOCK leva a um agente no host, que assina pela VM e nunca entrega a
chave.
O Gheist não consegue usar o substituto
A ferramenta do Gheist roda na infraestrutura do próprio atacante. Ela chama o
api.github.com com o token que comprou e espera ver repositórios. Com um
substituto do Bromure, o GitHub responde 401 Bad credentials. O Bromure
deriva o substituto do token real com um salt que fica no seu Mac, e só o proxy
desse Mac sabe qual token real ele representa. A ferramenta não consegue listar
nem um repositório, então nunca cria uma branch.
Todo o método do Gheist consiste em perguntar o que um token alcança. Fora da VM em que foi gravado, um substituto do Bromure não alcança nada.
Um substituto a caminho de um stealer dispara um alarme
O stealer ainda precisa enviar o que encontrou para algum lugar, e, a partir de
uma VM do Bromure, toda conexão HTTPS passa pelo proxy no Mac. O proxy examina
cada requisição de saída, cabeçalhos e corpo, em busca de substitutos indo para
um host para o qual não foram criados. Um substituto ghp_ com destino a
qualquer coisa que não seja o GitHub é bloqueado com HTTP 451 antes que um byte
saia do Mac. A VM é pausada, e um alerta exibe
This environment may have been compromised, com o nome da credencial, o
host a que ela pertence e o host para onde foi enviada.
Você pode desligar a máquina ou guardar o disco e a pasta pessoal dela para investigação. Uma máquina marcada como comprometida não inicia de novo até que o disco e a pasta pessoal sejam apagados, então o stealer não sobrevive à reinicialização.
Defina Unmatched traffic como Deny no painel Guardrails da máquina e libere os hosts de que o projeto precisa, e o host de upload do stealer é recusado de imediato, com arquivo criptografado ou não. A conexão recusada vira uma linha na Security Timeline.
As escritas de dentro da máquina esperam por você
Suponha que o malware desista do roubo e use o substituto onde ele funciona, de dentro da VM, através do proxy. Ele teria de fazer o que o Gheist faz: criar uma branch e fazer commit de um workflow.
As novas máquinas Bromure definem a política de escrita do GitHub como
Prompt before write. As leituras passam. Um git push ou uma escrita REST
no github.com para em uma caixa de diálogo no seu Mac, que mostra a operação
literalmente:
Allow write on "GitHub" from workspace "web-app"?
POST /repos/<owner>/<repo>/git/refs
Uma branch ci-sync- que você não pediu, surgindo enquanto você olhava um pull
request, é fácil de recusar. Don't allow devolve ao agente o mesmo erro que
uma política de bloqueio daria, e Allow once não concede nada além daquela
única escrita.
A rotação acontece em um só lugar
O primeiro passo de resposta da Wiz é trocar todo segredo que um repositório afetado podia alcançar e revogar os tokens que enviaram os commits. "Apagar a execução ou a branch não desfaz a exposição."
Em um Mac com Bromure, o lado do desenvolvedor dessa rotação é um campo só. O
token real do GitHub fica em uma única entrada criptografada no host. Cole o
novo no painel Credentials da máquina e a próxima inicialização grava o
substituto correspondente; não há cópias para caçar em perfis de shell,
arquivos .env ou configurações de agentes. Para uma chave guardada como
referência do 1Password (op://vault/item/field em Other API keys), o
Bromure a resolve de novo a cada dois minutos, então uma rotação no 1Password
chega às máquinas em execução sem reinicialização.
O próprio cofre fica protegido por uma chave no Data Protection Keychain do macOS, vinculada à identidade de assinatura do Bromure e nunca sincronizada. Um stealer que copia a pasta do Bromure para fora do Mac leva só texto cifrado.
Deixe um agente fazer a caça, só leitura
Os passos de caça da Wiz são um trabalho tedioso em muitos repositórios, o que
os torna um bom trabalho para um agente: listar toda branch que corresponda a
^ci-sync-[0-9a-z]{6}$, encontrar os commits que adicionam apenas
sync-check.yml com a mensagem ci: sync config, puxar do log de auditoria as
execuções apagadas menos de um minuto depois de terminar e as que usam o user
agent gheist.
Entregue a tarefa a uma máquina Bromure cuja política de escrita do GitHub
esteja em Read-only. O agente pode ler todo repositório e log de auditoria
que o token permite, e qualquer push ou escrita REST é recusado no Mac antes de
chegar ao GitHub. Se o agente fizer checkout de uma branch ci-sync- para
examiná-la, inclusive uma das branches que a Wiz viu reenviadas com loaders do
PolinRider, o que rodar roda em uma VM cheia de substitutos. Cada troca de
credencial aparece como uma linha Credential brokering na Security
Timeline, então dá para ver depois quais hosts a caça tocou.
Os stealers encontram substitutos
GH_TOKEN, a configuração do gh e ~/.git-credentials guardam um
ghp_ falso. O token real fica criptografado no Mac.
O Gheist recebe um 401
Fora da máquina, o substituto não é um token do GitHub. A ferramenta não consegue listar repositórios, então não planta nada.
Vazamentos pausam a VM
Um substituto indo para qualquer lugar que não seja o GitHub é bloqueado com um 451, e a VM fica pausada até você decidir o que fazer com ela.
As escritas perguntam antes
As novas máquinas perguntam antes de cada push e escrita REST no GitHub, com a requisição exata na tela. Uma caça pode rodar só com leitura.
Tire o token do notebook
A retenção com aprovação do GitHub funcionou nos repositórios públicos, e a lista de reforço da Wiz é a certa: revisores obrigatórios nos ambientes de produção, confiança OIDC restrita a branches protegidas, GitHub Apps ou tokens granulares de curta duração em vez de tokens de acesso pessoal. Tudo isso reduz o que um token roubado consegue fazer quando alguém o tem em mãos.
O Bromure Agentic Coding age um passo antes. Seus agentes enviam branches e abrem pull requests o dia todo, e o token que eles usam nunca fica em um disco que um stealer possa ler. O Gheist só consegue plantar workflows com tokens que ainda funcionam, e o de uma máquina Bromure só funciona pelo proxy daquela mesma máquina.
Para ver como esses mesmos substitutos resistem a um stealer feito para os arquivos de agentes de código, leia O stealer levou os substitutos. Para um único token roubado do GitHub que terminou em uma release envenenada, leia O token do agente era o do proprietário.