Ninguém publicou uma nova versão
O comunicado da Wordfence de 9 de agosto sobre o comprometimento da BdThemes descreve um ataque à cadeia de suprimentos sem cadeia de suprimentos nenhuma. Nenhum lançamento, nenhum script de instalação, nenhum typosquat, nenhum arquivo modificado no repositório do plugin. Os atacantes obtiveram acesso de escrita ao bucket de armazenamento do fornecedor e alteraram um feed JSON que 350.000 sites buscavam a cada carregamento de página do painel administrativo. Para quem desenvolve, a pergunta que isso levanta é o que a máquina que roda o seu agente de código tem permissão de escrever, e para quem.
Todo controle de cadeia de suprimentos que você possui inspeciona coisas que foram publicadas. Este não publicou nada, e mesmo assim alcançou 350.000 sites.
Em 7 de agosto de 2026, a Wordfence começou a ouvir relatos de sites WordPress rodando plugins da BdThemes onde brotavam contas de administrador que ninguém havia criado. A lista chega a sete plugins: Element Pack Addons for Elementor, Prime Slider, Pixel Gallery, Ultimate Post Kit, Ultimate Store Kit, Live Copy Paste e Smart Admin Assistant. Só o Element Pack passa de 100.000 instalações ativas, e os sete juntos ultrapassam 350.000.
Em 8 de agosto o fornecedor já havia limpado sua API e o WordPress.org já havia retirado todos os sete plugins enquanto durasse a investigação. A Wordfence publicou seu PSA sobre o comprometimento no dia seguinte, e sua conclusão central é uma negativa: ninguém havia tocado no código dos plugins.
Nada foi publicado
Os plugins da BdThemes carregam um componente interno chamado Biggopti cujo trabalho é pequeno e monótono: buscar banners promocionais da API remota do fornecedor e desenhá-los no painel administrativo do WordPress. Os dados do banner são um arquivo JSON estático em um bucket do DigitalOcean Spaces, servido por trás da Cloudflare.
Os atacantes conseguiram acesso de escrita a esse bucket. Essa é toda a intrusão.
O Biggopti pegava o campo display_id da resposta JSON e o soltava dentro de um
atributo HTML id sem escapá-lo, uma falha de cross-site scripting que a
Wordfence classificou como
CVSS 5.4, média
e rastreou até 1º de março de 2026, quando um desenvolvedor a escreveu uma vez e
a copiou de um plugin para o seguinte. Um display_id carregando um manipulador
onanimationstart é, portanto, JavaScript, e ele roda no navegador de todo
administrador logado, a cada carregamento de página do wp-admin, em todo site
com o plugin instalado.
A carga usou bem esse assento. Um script chamado w2.js pedia instruções de
alvo a um servidor de comando e controle, a máquina que um atacante usa para
dizer a uma carga quais sites valem a pena tomar, e então criava contas de
administrador ocultas pela API REST do WordPress, montado na própria sessão e
nos nonces do administrador de verdade. Uma variante derivava nomes de usuário
previsíveis do nome de host do site. Um plugin falso, muitas vezes chamado de
algo como wp-smart-thumbnails, entregava um web shell em emer-run.php, ou
seja, um script que um atacante consegue pilotar carregando uma URL comum.
Must-use plugins, que o WordPress carrega a cada requisição e esconde da tela de
plugins, carregavam um backdoor de login mágico e um módulo que ocultava as
contas novas reescrevendo os totais de usuários que a tela administrativa
exibe.
Os carimbos de tempo nos registros envenenados colocam o início mais antigo possível em 23 de junho de 2026, sete semanas antes de alguém perceber.
Alinhe isso com os controles que uma equipe cuidadosa mantém. Fixação de versão: as versões nunca mudaram. Lockfiles e hashes de integridade: batiam, porque os arquivos que eles cobriam estavam intocados. Revisão de script de instalação: não houve instalação. Detecção de typosquat: nome certo, fornecedor certo, entrada certa no repositório. Revisão de código do diff entre lançamentos: nenhum diff, porque nenhum lançamento. Certificado e CDN: ambos em perfeita saúde. Cloudflare e TLS provaram que o JSON chegou ao site sem modificação, vindo do bucket de onde deveria vir, que é precisamente o que o tornava útil.
O incidente começa com uma credencial que podia escrever
Leia o comunicado do lado do fornecedor e a cadeia de ataque encurta para um passo. Alguém obteve acesso de escrita a um bucket de armazenamento. Todo o resto é consequência.
Comprometimentos de upstream continuam chegando nesse formato. O verme do escopo npm da Red Hat, o repositório que realmente era da Microsoft, os marketplaces de extensões, os registros de contêineres: em cada um deles a carga é a parte que vira reportagem, e a parte que importava era alguém acabar com uma credencial capaz de sobrescrever um artefato que outras pessoas buscam periodicamente.
Onde vive uma credencial dessas? No ambiente de quem desenvolve. Um
DIGITALOCEAN_ACCESS_TOKEN num shell, uma configuração do doctl em
~/.config, um login de registro em ~/.docker/config.json, um perfil da AWS,
um token do GitHub em ~/.git-credentials, uma chave SSH que pode dar push. Num
laptop, num terminal, ao lado do projeto. Em 2026, ao lado do projeto é também
onde você roda seu agente de código.
A maioria dos textos deste blog descreve um jeito de virar esse agente contra você. Um relatório de bug que rodou um comando. Uma página web que reescreveu a configuração do agente. Um comentário que ninguém conseguia ver. Uma skill que rodou com a sua identidade. Três bypasses de harness numa única palestra da Black Hat. Tome o catálogo como dado, e assuma que numa terça qualquer um deles cai em cima de você.
O quanto essa terça fica ruim depende de uma pergunta que você pode responder hoje, sem saber qual injeção vai vencer: o que esta máquina tem permissão de escrever, e para quem? Num laptop a resposta é tudo o que você tem permissão de escrever, e se você publica qualquer coisa, isso inclui o artefato que várias centenas de milhares de estranhos buscam sem olhar.
O token não está na máquina
O Bromure Agentic Coding roda o agente em uma VM Linux descartável no Apple Silicon, e cada byte do seu tráfego cruza um proxy no host, fora da caixa em que o agente roda. O Bromure arranja as credenciais em volta dessa fronteira em vez de colocá-las dentro dela.
Coloque seu token de acesso pessoal do DigitalOcean no painel Credentials e
o Bromure mantém o valor real no Mac. O que aterrissa na VM é um falso: uma
variável de ambiente DIGITALOCEAN_ACCESS_TOKEN e um
~/.config/doctl/config.yaml que fazem o doctl funcionar sem
doctl auth init, guardando um valor de espaço reservado. Quando a VM faz uma
requisição para api.digitalocean.com, o proxy do host substitui o token real
no fio, para aquele destino, e mais nada.
Qualquer coisa rodando dentro daquela VM pode ler o ambiente, dar grep nos
dotfiles e percorrer o disco inteiro: o agente, uma dependência, um passo de
build, um comando de shell que saiu de uma issue envenenada. O que ela coleta é
brm_…. O mesmo arranjo cobre o resto do conjunto: tokens do GitHub, GitLab e
Bitbucket, logins de registro de contêiner escritos como um blob base64 falso em
~/.docker/config.json, um kubeconfig sintético com certificados de cliente
descartáveis, o Linear, os endpoints HTTPS de banco de dados, e qualquer outra
coisa que você adicione em Other API keys. A AWS vai além: o host reassina
cada requisição com SigV4 usando o segredo real, de modo que contornar o proxy
rende uma InvalidSignatureException em vez de uma escrita não autorizada.
Guardrails decide o verbo, no Mac
Este incidente precisou de uma operação: sobrescrever um objeto num bucket.
Guardrails é um motor de políticas dentro do proxy do host, com um modo
por recurso: Off, Block destructive ou Read-only. Configure o DigitalOcean
como Read-only e toda mutação para api.digitalocean.com volta como um 403
duro que o agente reporta como uma falha comum de API. O Bromure toma essa
decisão no macOS. Não há configuração dentro da VM para mudar, nem variável
de ambiente para desativar, nem arquivo para editar.
Publicar pergunta antes
Toda entrada de credencial tem Require approval to use. Ligue isso e cada troca falso→real levanta um diálogo de consentimento no host antes que o valor real chegue ao fio, com concessões limitadas no tempo no caminho SSH: cinco minutos, uma hora, o resto da sessão. Dar push num lançamento é algo que você quis fazer, e custa um clique. Uma escrita que você não iniciou aparece como um diálogo que você não esperava, num momento em que você não esperava nenhum.
Registros e git recebem o mesmo tratamento
O Guardrails classifica o tráfego de registro de contêiner por método
contra o próprio hostname do registro: GET e HEAD são um pull, PUT e
POST são um push, DELETE é destrutivo. GitHub, GitLab e Bitbucket estão
cobertos tanto na API REST quanto no git sobre HTTPS, onde
git-receive-pack conta como escrita e é bloqueado no modo Read-only
enquanto os fetches sempre passam. Um perfil que lê todo o seu grafo de
dependências e não dá push em nada são duas configurações.
O ambiente é uma lista que você escreveu
Um perfil do Bromure enxerga as pastas do Mac que você compartilhou com ele,
até oito, cada uma montada sob /home/ubuntu. Todo o resto no seu Mac não
está montado, não é alcançável e não é enumerável. O conjunto de credenciais
é aquele que você adicionou a esse perfil, de modo que um perfil que você
usa para construir um frontend não guarda credencial de cluster alguma para
nada encontrar, oculta ou não.
A busca em tempo de execução também cruza o proxy
Agora pegue o lado de baixo da história, onde você é um dos 350.000. O que você consegue pegar ali depende de onde você coloca a inspeção.
O painel Supply Chain do Bromure filtra buscas de pacote em npm, PyPI,
Cargo, RubyGems, Maven, NuGet, módulos Go e Packagist: uma barreira de idade
que vem ligada por padrão e recusa qualquer coisa publicada nos últimos dois
dias, consultas ao OSV, socket.dev ou Delpi como provedor de filtragem,
remoção de scripts de instalação que reescreve o tarball e conserta o hash dos
metadados do registro para que o npm ainda verifique, e um aviso antes que
tarballs fixados por lockfile passem sem modificação. O .npmrc e o pip.conf
dentro da VM podem apertar essas regras e não podem afrouxá-las.
Essas regras se pagam por causa de onde o Bromure as aplica. O proxy do host as aplica a uma requisição em vez de a um pacote, de modo que um arquivo JSON puxado de um bucket de fornecedor em tempo de execução por um passo de build, uma CLI, um servidor MCP ou a própria aplicação cruza a mesma fronteira que um tarball. Tudo o que a VM diz à rede cruza essa fronteira.
Isso muda o aspecto das sete semanas. Com o Session trace em Activity, o Bromure registra o host, o status, a latência, o relatório de troca e quaisquer avisos de vazamento para cada requisição que a VM faz. Coloque em Everything e ele guarda também os corpos, para todo host, cifrados com a mesma chave de keychain dos segredos do seu perfil e legíveis no Trace Inspector. O dia em que um feed do qual seu build depende começa a responder diferente é um diff que você pode abrir, no Mac, num log ao qual a VM não tem acesso de escrita.
Um feed envenenado é também, no fim das contas, texto chegando a uma máquina.
Quando ele alcança o agente como um tool_result, um arquivo, uma página
buscada ou um arquivo de instruções como CLAUDE.md ou AGENTS.md, o painel
Prompt Injection o pontua no dispositivo com um modelo local, e nada sai do
Mac. Escolha Ask me what to do e a requisição pausa com o trecho sinalizado
à mostra; escolha Block unilaterally e o agente recebe um 451 duro. Quando
você quiser a máquina fora do caminho, Erase home reinicia /home/ubuntu e
Reset to base reclona o disco de sistema do workspace a partir da imagem
base.
A BdThemes vai corrigir o escape, o bucket vai ganhar chaves mais apertadas, e a próxima versão desta história vai chegar com outro fornecedor e outro feed. O que dura é o arranjo por baixo. Seu ambiente de desenvolvimento guarda um punhado de credenciais, cada uma capaz de reescrever algo que milhares de pessoas buscam periodicamente, e esse mesmo ambiente agora roda um agente lendo texto de estranhos o dia inteiro.
A maioria dos textos aqui pergunta o que seu agente deveria poder ler. Responda também à outra pergunta, a que é desconfortável num laptop e fácil num perfil: o que ele tem permissão de escrever, e para quantas pessoas?
Instale o Bromure Agentic Coding, e dê ao agente uma máquina que não guarda a chave de nada que você publica.