Voltar para todas as publicações
Publicado em · por Renaud Deraison

Um a cada três dias

A StepSecurity divulgou no sábado a contagem de um ano de ataques com pacotes maliciosos: 56 deles, 6 nos primeiros seis meses e 50 nos seis meses e meio desde fevereiro, cerca de um a cada três dias desde março. Depois nomeou o motivo pelo qual continuam a funcionar: segredos nas máquinas de desenvolvimento e nos runners de CI, versões latest não fixadas, acesso de saída à rede sem restrições. Nenhuma dessas coisas é uma propriedade de um pacote. As três descrevem a máquina em que a instalação corre, e a máquina que mais pacotes instala hoje tem um agente de programação a bordo.

Um pacote malicioso dispara na instalação. Não há caminho de código vulnerável a alcançar, nem exploit a escrever, nem janela para corrigir. A um a cada três dias, o pacote em que ele chega deixou de ser a pergunta útil a fazer.

No sábado, a StepSecurity publicou uma contagem de doze meses de ataques à cadeia de fornecimento de código aberto. Cinquenta e seis. “Nos seis meses de agosto de 2025 a janeiro de 2026, registámos 6 incidentes. Nos seis meses e meio desde fevereiro de 2026, registámos 50.” Desde março, o ritmo manteve-se em cerca de nove por mês, ou “aproximadamente um a cada três dias”.

A StepSecurity vende um registo com filtragem, portanto leia o relatório sabendo o que ele está a vender. A contagem aguenta-se mesmo assim. É o levantamento público mais cuidadoso de pacotes maliciosos que alguém publicou, e a definição por trás dele é estreita de propósito.

O que o número está a contar

O relatório deita fora as vulnerabilidades. Sem CVEs, sem um bloco unsafe que afinal era. Conta pacotes que alguém envenenou de propósito.

É por isso que o número é utilizável. Uma vulnerabilidade precisa de um segundo evento: alguém tem de encontrar a sua instância, alcançar o caminho de código afetado e tirar proveito. Pode correr uma versão vulnerável durante um ano e nada acontece. Um pacote malicioso salta tudo isso. Como diz o relatório, ele “corre no momento em que uma máquina de desenvolvimento ou um runner de CI o instala”. A instalação é a exploração. Nunca se obtém uma janela entre divulgação e correção, porque ninguém divulgou nada e ninguém lançou uma correção. Obtém-se um número de versão e depois uma correria para descobrir quem o descarregou.

Março de 2026 teve 13. Abril 9, maio 9, junho 10, julho 6. Em cinco dias de março, uma operação seguida como Team PCP levou 78 330 segredos de 2 186 organizações, 92 delas empresas cotadas em bolsa, envenenando 76 das 77 tags da GitHub Action do Trivy e um punhado de outros componentes de build. O axios, com mais de 100 milhões de downloads semanais, lançou duas versões maliciosas com um trojan de acesso remoto multiplataforma. Seis worms aparecem no ano: Shai-Hulud duas vezes, CanisterWorm, Mini Shai-Hulud, Miasma e ChainDrop este mês. npm, PyPI, RubyGems, Composer, crates.io, GitHub Actions. “Todos os grandes ecossistemas de pacotes e plataformas de CI/CD foram atingidos.”

Duas entradas dessa lista importam aqui. O relatório nota que o hackerbot-claw, “um bot movido a IA”, foi atrás das GitHub Actions da Microsoft, da Datadog e da CNCF, e que “o worm Miasma visava especificamente agentes de programação com IA”.

As três condições, e nenhuma delas é um pacote

Depois o relatório faz a coisa útil. Sob um título que pergunta por que razão os ataques continuam a funcionar, dá três respostas: “segredos em pipelines de CI/CD e máquinas de desenvolvimento, versões latest não fixadas, acesso de saída à rede sem restrições”.

Nada nessa lista descreve um pacote. Sem ofuscação, sem distância de typosquatting, sem esperteza no hook de instalação. As três descrevem a máquina que corre a instalação: o que ela guarda, como escolhe uma versão e onde tem permissão para ligar. Mude uma delas e a maior parte dos cinquenta e seis deixa de compensar.

Latest não fixado

Alguma coisa na cadeia de resolução diz “a mais recente”. Um intervalo flutuante, uma dependência transitiva, um lockfile reconstruído, um agente a acrescentar uma biblioteca em seu nome. O atacante só precisa que o resolvedor prefira uma versão publicada há minutos.

Segredos na máquina

~/.ssh, ~/.aws, ~/.kube, ~/.npmrc, um GH_TOKEN no ambiente, um login de registo em ~/.docker/config.json. Nada disso pertence ao build, e o que o build acabou de arrancar consegue ler tudo.

Saída aberta

O material roubado tem de sair, e o binário de segunda fase tem de chegar. Ambos são ligações de saída vulgares a partir de uma máquina que permite ligações de saída para toda a parte.

O que tem de se alinhar1 · o pacoteuma versão envenenadaé publicada e vive umahora ou duas2 · o resolvedoralgo na cadeia pede a maisrecente, e a mais recenteé a envenenada3 · o discoo código de instalação lê~/.ssh ~/.aws ~/.npmrce o ambiente4 · o fiouma ligação de saídaleva tudo e traz a segundafase de voltaos passos 2, 3 e 4 são decididos pela sua máquina, não pelo pacote“segredos em pipelines de CI/CD e máquinas de desenvolvimento, versõeslatest não fixadas, acesso de saída sem restrições” — StepSecurityesta é a únicaparte que nãoconsegue controlar
Um ataque com pacote malicioso precisa de quatro coisas alinhadas, e só a primeira diz respeito ao pacote. As outras três são propriedades da máquina: como escolhe uma versão, o que guarda e para onde tem permissão para ligar.

De quem é a máquina que mais pacotes instala hoje

A defesa em que toda a gente se apoiava era a atenção. Lê-se o diff. Repara-se que uma biblioteca de cache apanhou uma dependência com o nome de um formatador de datas com uma letra trocada.

Duas coisas ultrapassaram isso. O ritmo, primeiro: ninguém revê dependências transitivas de três em três dias. Depois quem faz a instalação. Um agente de programação acrescenta uma biblioteca porque lhe pediu para construir alguma coisa, resolve a árvore, corre a instalação e relata que os testes passam. Você lê o diff que ele produziu, que é a parte que merece o seu tempo. Um tarball nunca aparece num diff. A lista da StepSecurity já inclui um worm que foi à procura desse modo de trabalho.

Fique com o agente. Mova o controlo para onde a velocidade dele deixa de importar.

Três condições, três controlos, todos do lado do Mac

O Bromure Agentic Coding corre o agente numa VM Ubuntu descartável sobre a framework Virtualization da Apple, com apenas as pastas que escolheu montadas lá dentro e um proxy do lado do anfitrião como única via de saída. Essa colocação põe cada uma das três condições da StepSecurity diante de um controlo que o build não consegue alcançar.

O latest não fixado passa a ser um relógio. Cada descarga de pacote atravessa primeiro o proxy do anfitrião: npm, PyPI, Cargo, RubyGems, Maven, NuGet, módulos Go, Packagist. O filtro de idade está ativo por omissão em dois dias. As referências flutuantes resolvem para a versão mais recente anterior ao limite, portanto latest continua a funcionar e deixa de significar “publicada enquanto lia isto”. Uma referência fixada a algo demasiado fresco volta como um 451 com um erro do Bromure. Além disso, pode ligar consultas OSV no limiar de severidade que quiser, encaminhar pacotes pela base de reputação da socket.dev ou pelo registo com filtragem da Delpi, e retirar preinstall, install, postinstall e prepare dos tarballs npm em tempo real. O Bromure reescreve o tarball e atualiza o hash dos metadados do registo para que a verificação do próprio npm continue a passar. O .npmrc e o pip.conf dentro da VM podem apertar qualquer uma destas definições e não as podem afrouxar.

Os segredos na máquina são falsos. As credenciais reais nunca entram na VM. O proxy troca um marcador pelo token verdadeiro no fio; o kubeconfig no convidado é sintético, com certificados de cliente descartáveis; o AWS recebe um auxiliar credential_process que entrega ao SDK um falso de quarenta caracteres enquanto o anfitrião reassina o SigV4 com o material real, portanto tudo o que contorne o proxy colhe uma InvalidSignatureException da Amazon. O código de instalação que lê ~/.aws/credentials encontra um ficheiro, lê-o e exfiltra um engodo. Cada credencial leva também uma política de escrita (Desligada, Bloquear operações destrutivas ou Só leitura) aplicada no proxy do anfitrião, mais Exigir aprovação para usar, que transforma qualquer utilização única numa caixa de consentimento com uma autorização de cinco minutos, uma hora ou o resto da sessão.

A saída aberta responde a uma regra que você escreveu. Em Salvaguardas, a tabela Ligações de saída é uma firewall por perfil: uma linha por regra, cada uma com uma ação, um protocolo (tcp, udp, web ou any), um anfitrião ou CIDR, portas e, para web, uma lista de métodos HTTP. As regras são avaliadas de cima para baixo, ganha a primeira correspondência, e Tráfego sem correspondência é um interruptor que põe em Permitir ou Negar. Duas camadas aplicam o mesmo conjunto de regras: o switch virtual por IP de destino e pelos nomes de anfitrião que aprendeu a espreitar nas respostas DNS do convidado, e o proxy pelo SNI de TLS. A interceção transparente está ativa por omissão, portanto um script de build que desative o HTTPS_PROXY não muda nada quanto às ligações que saem.

O ataque à crates.io da semana passada mostra o que essa regra apanha. Na análise da StepSecurity, o script de build do dropper descodificava um URL em base64 e ia buscar um binário específico da plataforma a um IP nu na porta 9089, com validação de certificado desativada, e depois destacava o processo filho para que o Cargo não esperasse por ele. Com o tráfego sem correspondência negado, essa descarga nunca se completa, e o código de saída do script de build deixa de ser uma pergunta interessante.

Uma instalação, duas máquinasNo portátilo resolvedorleva o que foi publicado por últimoos scripts de instalaçãocorrem como você, antes de qualquer importas credenciais~/.ssh ~/.aws ~/.npmrc $GH_TOKENo fiochega a qualquer anfitrião, sem registoNum espaço de trabalho Bromurefiltro de idade, ativo por omissão em dois diasreferências flutuantes ficam sob o limite · umaversão fixada demasiado fresca recebe um 451retirados do tarballhash dos metadados reescrito, o npm continua a verificarfalsas, trocadas no fioos valores reais ficam no Mac · SigV4 reassinadosem correspondência: negar · Registo de segurança
Os mesmos quatro passos, corridos dentro de um espaço de trabalho Bromure. O passo um continua a acontecer, porque ninguém pode impedir alguém de publicar uma versão má. Os passos dois, três e quatro encontram cada um um controlo que vive no Mac, fora de tudo aquilo em que a instalação pode escrever.

Oitenta e seis minutos

Volte ao mesmo ataque para perceber por que razão um relógio bate um juízo de valor. Os lançamentos envenenados de arrayref, internment e append-only-vec viveram 86, 90 e 107 minutos antes de a crates.io os retirar. Para encaminhar os resolvedores até eles, o atacante retirou as versões limpas, da 0.3.9 até à 0.3.5, numa rajada que demorou dezasseis segundos, deixando um único lançamento que ninguém tinha retirado. A frase mais afiada da StepSecurity sobre o incidente: “O atacante transformou a própria funcionalidade de segurança do registo no canal de entrega.” E o arrayref em si não trazia código malicioso. A carga útil entrou por um typosquat de proc-macro2 em tempo de build.

Um filtro de idade de dois dias nunca tem de reconhecer nada disso. Uma versão com oitenta e seis minutos é mais nova do que o limite e, quando tiver idade suficiente para ser instalada, a crates.io já a apagou. O atacante construiu uma janela mais curta do que aquela que o filtro ignora.

Ataque cinquenta e sete

O relatório fecha com uma frase que vale a pena guardar: “56 ataques em 12 meses. O ataque número 57 já está a ser construído.”

Não vai saber que pacote o transporta, e não o vai ler. O seu agente também não. Cinco meses a um a cada três dias correm muito acima da cadência a que alguém revê, e chamar-lhe descuido não leva ninguém a lado nenhum. Você decide, isso sim, sobre o que ele aterra: se a versão de que precisa é uma que o seu resolvedor aceitará, se os segredos que vem procurar são reais, e se o endereço que quer alcançar é um para o qual a sua máquina encaminha. Três definições, todas do seu lado da fronteira.


Fontes: StepSecurity, “The State of Open Source Supply Chain Attacks” (22 de agosto de 2026) · StepSecurity, “Rust Supply-Chain Attack: arrayref, internment, and append-only-vec Poisoned by the proc-macro1 Build-Time Dropper” (20 de agosto de 2026) · StepSecurity, “Team PCP Stole 78,330 Secrets From 2,186 Organizations” (13 de agosto de 2026)