O pacote era mesmo da Red Hat
Entre o fim de maio e 1 de junho de 2026, um worm chamado Miasma empurrou código de roubo de credenciais para 32 pacotes sob o escopo npm @redhat-cloud-services — o namespace da própria Red Hat, ~117.000 downloads semanais, assinado pelo pipeline de publicação real da Red Hat. Não havia typosquat a pegar nem mantenedor desconhecido a sinalizar. O sinal de confiança era o nome do fornecedor no escopo, e o nome do fornecedor é exatamente aquilo em que o atacante entrou montado. Eis por que «prefira publicadores reputados» deixou de ser uma defesa, e o que muda quando o agente que roda a instalação vive em uma VM Bromure por perfil.
Os ataques de cadeia de suprimentos que documentamos em maio tinham
um indício. Uma URL Git onde deveria estar um range de versões. Um
runtime Bun aparecendo do nada. Uma dependência opcional que falhava
de propósito. O Miasma não tem indício. Os pacotes eram
@redhat-cloud-services/sources-client e trinta e um de seus irmãos —
genuinamente da Red Hat, sob o próprio escopo npm da Red Hat,
publicados pelo próprio pipeline da Red Hat com uma assinatura
válida. Nada foi falsificado. O atacante não precisou falsificar
nada. O nome no escopo era o exploit inteiro.
Um agente de codificação resolvendo @redhat-cloud-services/vulnerabilities-client
não hesita, e nem você hesitaria. É uma dependência de primeira parte
de um dos maiores fornecedores corporativos da indústria. Não há
mantenedor a verificar, porque o mantenedor é a Red Hat. Não há nome
a apertar os olhos em busca de uma letra trocada, porque o nome está
soletrado exatamente da forma que deveria estar. Toda heurística que
um desenvolvedor cuidadoso ou um agente cuidadoso aplica antes de
rodar npm install retorna verde. Então a instalação roda, e um hook
preinstall dispara, e o hook é um blob de 4,2 megabytes de
JavaScript ofuscado que começa a ler o sistema de arquivos em busca
de chaves.
O incidente inteiro é uma demonstração de um fato incômodo: publicador reputado não é um controle de segurança. Parece um. A maior parte dos conselhos de cadeia de suprimentos dos últimos três anos se apoia nele. E em 29 de maio ele não comprou absolutamente nada.
O que o Miasma fez.
A campanha — a string Miasma: The Spreading Blight aparece pela
primeira vez em um commit datado de 29 de maio de 2026, segundo a
OX Security —
foi pega pela Aikido e pela OX Security e
depois analisada por Socket, JFrog, Wiz, ReversingLabs, Microsoft e
outros. A BleepingComputer
e a The Hacker News
cobriram ambas no dia 1 de junho.
A forma, reduzida à sua mecânica:
- O ponto de entrada foi uma conta GitHub comprometida de um
funcionário da Red Hat, usada para empurrar commits maliciosos
para os repositórios fonte de
@redhat-cloud-services. - Um workflow do GitHub Actions carregava um script
_index.jsque se autenticava no endpoint de publicação confiável do npm usando um token OIDC — o mesmo mecanismo sem chave que o npm agora recomenda em vez de tokens de publicação de longa duração. Do ponto de vista do npm, a CI da Red Hat publicou os pacotes da Red Hat. A assinatura era real. - Os pacotes publicados carregavam um hook
"preinstall": "node index.js"e um payload ofuscado de aproximadamente 4,2 MB. - Na instalação, o payload varria em busca de segredos do GitHub
Actions, credenciais AWS, credenciais Google Cloud, service
principals do Azure, tokens do HashiCorp Vault, tokens de
service-account do Kubernetes, tokens de publicação npm e PyPI,
chaves SSH, credenciais Docker, chaves GPG e arquivos
.env— depois criptografava e exfiltrava o que quer que encontrasse. - Ele se autopropagava usando o acesso roubado para fazer commit
através da API do GitHub, lendo arquivos
action.ymlpor GraphQL e escrevendo novos workflows de volta por mutations, de modo que as mudanças apareciam, nas próprias palavras da Red Hat no log de commits, verificadas e assinadas.
No total, 32 pacotes em 96 versões foram atingidos, pacotes com
cerca de 117.000 downloads semanais, e a campanha mais ampla
tocou 309 repositórios do GitHub. A avaliação da Socket foi que
isto é «efetivamente uma campanha Mini Shai-Hulud: usa as mesmas
táticas centrais de execução em tempo de instalação, coleta de
credenciais, alvo de CI/CD, exfiltração criptografada e potencial
propagação a jusante.» A declaração da Red Hat foi que «os pacotes
são estritamente limitados ao desenvolvimento interno, e o código
malicioso nunca foi publicado para consumo de clientes» — o que é
verdade, e também não é grande consolo para os desenvolvedores e
runners de CI que puxaram @redhat-cloud-services/* como dependência
transitiva na janela antes de os pacotes serem removidos.
A defesa que todo mundo recomenda é a que quebrou.
Escrevemos sobre um worm similar
há três semanas — o comprometimento
do TanStack, onde o indício era um script prepare pendurado em uma
URL Git fixada e um runtime Bun que apareceu do nada. A lição honesta
daquele post foi: não confie no lockfile, não confie na assinatura. O
Miasma é a próxima volta do mesmo parafuso, e vale a pena ser preciso
sobre o que é diferente, porque a diferença é o ponto inteiro.
A pilha padrão de higiene de cadeia de suprimentos tem três degraus.
Fixe suas versões. Verifique a proveniência. Prefira publicadores
reputados. O Miasma atravessa direto pelos três. Fixar não faz nada,
porque as versões maliciosas são as versões publicadas. A
proveniência não faz nada, porque a proveniência é válida — o fluxo
de publicação confiável OIDC era genuinamente a CI da Red Hat, e a
jusante o worm cunhou commits de workflow que o próprio GitHub marcou
como verificados e assinados. E o terceiro degrau, prefira
publicadores reputados, não é apenas derrotado aqui — ele é a
superfície de ataque. A reputação do escopo @redhat-cloud-services é
a razão pela qual os pacotes são puxados sem um segundo olhar. Quanto
mais confiável o namespace, mais útil ele é para quem o assume.
Não existe versão de «ler o pacote com mais cuidado» que pegue isso. O pacote está bem. O pacote é da Red Hat. O problema é que execução de código em tempo de instalação com as credenciais ambientes do desenvolvedor é o contrato, e um nome confiável não faz nada para mudar o que esse código pode tocar quando roda.
A mesma instalação dentro do Bromure Agentic Coding.
O Bromure Agentic Coding roda seu agente de
codificação dentro de uma VM Linux por perfil — seu próprio
kernel, seu próprio sistema de arquivos, sua própria pilha de rede,
no framework de Virtualização da Apple. Um perfil é um escopo
coerente de trabalho: este cliente, este produto interno, esta
biblioteca open-source. O agente faz seu npm install ali dentro, e
o host — seu keychain real, suas credenciais de nuvem reais, suas
chaves SSH reais — está do outro lado de uma fronteira imposta por
hardware que o hook preinstall não pode atravessar.
As credenciais não vivem no perfil. Elas vivem no host, atrás de um
intermediário de credenciais.
Quando o agente precisa empurrar um commit ou publicar um pacote, ele
não lê um token do sistema de arquivos do convidado — não há nenhum
para ler. Ele pede ao intermediário, por um socket de domínio Unix,
para usar uma credencial em seu nome. O intermediário mantém a
chave privada real do GitHub App, cunha um token de instalação de
curta duração com escopo para o repo em que o agente já estava
trabalhando, e — para um perfil configurado para exigir isso —
apresenta um prompt de autorização que o desenvolvedor responde no
host antes de a requisição sair. O token é cunhado e anexado à
requisição de saída inteiramente do lado host; ele nunca entra na
memória ou no disco do convidado. O princípio, que é tão antigo
quanto o ssh-agent: intermedie o uso da credencial, nunca seu
valor.
Então percorra a varredura do Miasma através dessa fronteira. O
node index.js lê ~/.aws/credentials e encontra um stub ou nada.
Ele lê ~/.npmrc e não encontra token de publicação. Ele lê o
ambiente em busca de $GITHUB_TOKEN e não encontra nada para roubar
— o token de instalação é cunhado e gasto no host, nunca escrito no
convidado, e o intermediário só cunha um quando o desenvolvedor
responde ao prompt de autorização. O keyring GPG, a chave privada
SSH, o token Vault, o kubeconfig: do lado host, intermediados se
expostos de algum modo, ausentes se não. O payload de 4,2 MB roda até
a conclusão exatamente como projetado. Ele apenas exfiltra um
convidado que nunca esteve segurando as chaves do desenvolvedor.
O push que o proxy se recusa a encaminhar.
Roubar a chave é apenas metade do Miasma. A outra metade é propagação: ele usou o acesso GitHub roubado para fazer commit de workflows envenenados de volta através da API, e foi isso que transformou uma instalação ruim em 309 repositórios. O intermediário lida com o roubo. Os Guardrails, adicionados no Bromure Agentic Coding 2.0, lidam com o uso indevido.
Os Guardrails são um motor de políticas do lado host que vive dentro
do mesmo proxy MITM por onde o tráfego intermediado já flui, de modo
que um agente comprometido no convidado não pode contorná-los. Cada
requisição é classificada pelo que ela de fato faz ao recurso, e cada
recurso — GitHub, AWS, Kubernetes, registros Docker, DigitalOcean,
GitLab, Bitbucket, bancos de dados hospedados — pode ser definido como
desligado, bloquear destrutivos ou somente leitura. Coloque o
guardrail GitHub de um perfil em modo somente leitura e um git push
— o git-receive-pack de que o worm precisa para escrever seu
workflow de volta — retorna um 403 duro, enquanto o git fetch
continua funcionando. Um DELETE contra a API do Kubernetes, uma
exclusão de manifesto em um registro, uma chamada Terminate* ao EC2:
mesmo tratamento. O agente apenas vê uma falha normal de API. O passo
de propagação do Miasma é uma escrita que o proxy declina encaminhar,
quer o agente tenha chegado perto de uma credencial real ou não.
E quanto à persistência?
É aqui que o modelo por perfil é honesto sobre um custo. O Miasma é um worm; sua ambição inteira é voltar. Em uma fantasia de disco descartável, você descarta isso — o disco se foi depois da tarefa. Um perfil Bromure é de longa duração, então um payload que se escreve em um script de inicialização dentro do perfil pode sobreviver até a próxima sessão de agente nesse perfil. Não vamos fingir o contrário.
O que essa persistência herda, contudo, é um convidado sem chaves do host e um intermediário que só fala em tokens de curta duração e com escopo limitado. O worm acorda no mesmo sandbox em que morreu. Ele pode ler os mesmos stubs. Ele pode pedir ao intermediário para usar uma credencial para o único repo para o qual este perfil está autorizado — e o desenvolvedor ainda tem que responder ao prompt de autorização para que isso vá a algum lugar, com os Guardrails livres para recusar o push completamente. Ele não pode alcançar o keychain do host, os outros perfis, ou as credenciais de nuvem do desenvolvedor, porque essas nunca estiveram dentro da fronteira para começar. Persistência compra presença continuada em uma caixa sem nada dentro.
E cada pedacinho disso — o preinstall disparando, o node index.js
carregando um blob de múltiplos megabytes, o arquivo escrito em um
caminho de inicialização, a tentativa de saída — aterrissa no trace
de sessão no nível do hipervisor.
Quando a Aikido publica os indicadores na manhã seguinte, a pergunta
«este perfil alguma vez rodou o Miasma?» é um grep, não um
engajamento de resposta a incidentes.
Onde isto não te salva.
O escopo do intermediário é o jogo inteiro.
Se um perfil está provisionado para publicar no seu escopo npm hoje, e esse perfil instala o Miasma hoje, o intermediário vai deixá-lo publicar. A intermediação funciona porque a concessão é estreita e de curta duração. Um perfil que não precisa publicar não deveria poder. Defina o escopo de propósito.
Ele não revisa o diff.
O Miasma se propagou fazendo commit de workflows que apareciam verificados e assinados. Um guardrail GitHub somente leitura bloqueia o push completamente — mas um perfil que legitimamente precisa empurrar roda em modo bloquear-destrutivos, e os Guardrails classificam por método, não pelo que está no diff. Nem o isolamento nem um guardrail no nível de método impede um agente de ser convencido a fazer commit de um workflow envenenado que ele tem permissão para empurrar. Leia o diff. O trace te diz quais diffs ler.
A área de transferência é compartilhada por padrão.
O Bromure vem com compartilhamento de área de transferência entre host e convidado ligado, porque colar um stack trace em um chat é algo que humanos fazem o dia todo. Para um perfil sensível, isole a área de transferência. O controle existe; só não é o padrão.
O trace é um log de auditoria, não um IDS.
O trace de sessão registra o preinstall, o blob, a saída. Ele não, por si só, decide que o destino é hostil. Ele captura o suficiente para que, uma vez que alguém nomeie o indicador, sua resposta esteja a dois segundos de distância.
O próximo escopo já é confiável.
A lição do worm TanStack foi que o lockfile e a assinatura não são
defesas. O Miasma adiciona o corolário incômodo: nem o publicador é. O
escopo @redhat-cloud-services não fez nada de errado ao ser
confiável — ser confiável é o propósito inteiro de um namespace de
fornecedor, e é exatamente o que o tornou digno de atacar. A próxima
campanha vai entrar montada em um escopo em que você confia tanto
quanto, assinada com tanta validade, por um pipeline que era
genuinamente do fornecedor até deixar de ser.
Você não pode consertar isso confiando com mais cuidado. Você
conserta arranjando as coisas de modo que «qual escopo publicou isto»
deixe de ser a pergunta da qual seu keychain depende. O Bromure
Agentic Coding é a configuração onde o agente faz
sua instalação dentro de uma VM por perfil, as credenciais reais ficam
no host atrás de um intermediário, e o pior que um hook preinstall
pode fazer é exfiltrar uma caixa que nunca esteve segurando suas
chaves. É gratuito, open-source, e disponibilizado hoje.