Uma campanha com 148 pacotes npm disfarçados de proxies web para estudantes transformou os navegadores dos visitantes em uma botnet de negação de serviço distribuída por cerca de duas semanas em maio, segundo uma nova pesquisa da JFrog.
Os pacotes não tinham como alvo os desenvolvedores que poderiam instalá-los.
Os operadores usaram o registro como hospedagem gratuita para um site proxy com armadilhas e deixaram que os estudantes, ao tentar driblar os filtros da escola, fornecessem o tráfego do ataque.
Os pacotes foram publicados com nomes como charlie-kirk, ilovefemboys e miguelphonk, cada um contendo um app proxy batizado de “Lucide” e apresentado como uma página de entrada de tutoria chamada Riverbend Tutoring ou Northstar Tutoring.
Na superfície, o proxy funcionava e permitia que estudantes burlassem filtros de conteúdo para acessar jogos e sites bloqueados.
Por baixo, carregava um carregador remoto de código cujo payload os operadores podiam trocar quando quisessem, além de um gerador de flood via WebSocket construído para falar o protocolo proxy Wisp.
Quem abria a página entrava no enxame sem saber.
Nada disso era executado no momento da instalação.
Os pacotes não tinham ganchos de ciclo de vida nem scripts nativos de build, e nunca foram feitos para serem importados por um projeto.
O worm autorreplicante Shai-Hulud, que atingiu mais de 500 pacotes em setembro de 2025, coletou segredos de desenvolvedores e se republicou usando tokens roubados.
Dias antes dele, um ataque de phishing contra o mantenedor conhecido como qix inseriu código para drenar carteiras no chalk, no debug e em outros 16 pacotes, que juntos somavam bilhões de downloads semanais.
Esses ataques disparam no instante em que um pacote é instalado e têm como alvo quem está construindo software.
Este ignora o pipeline de build e espera em uma aba do navegador.
Um alerta anterior da SafeDep catalogou 141 desses pacotes em maio e interpretou a operação como adware e abuso do registro: anúncios popunder, scripts de monetização de terceiros e rastreamento via Google Analytics adicionados a um proxy Scramjet voltado a estudantes.
A análise fazia sentido para o que estava visível na superfície.
A JFrog foi mais fundo.
A equipe desofuscou o pacote principal do aplicativo, um único bloco de JavaScript de 5,4 MB que se desdobrava em mais de 20.600 linhas legíveis, e recuperou payloads arquivados no Wayback Machine para reconstruir a linha do tempo da campanha.
Dois módulos ficavam abaixo do adware, ambos acionados antes que a interface em React fosse renderizada.
O primeiro, que a JFrog chama de G2, é um carregador remoto de scripts e busca código da forma mais insegura possível.
Ele puxa JavaScript de um repositório no GitHub por meio da CDN jsDelivr, aponta para a branch main mutável em vez de um commit fixo, não usa verificação de Subresource Integrity e executa o que receber com os privilégios de origem do próprio site proxy, com acesso total a cookies, local storage e endpoints da mesma origem.
Uma política de no-referrer impede que a requisição revele de onde veio.
Quem controla a conta no GitHub por trás disso pode mudar, a qualquer momento, o código executado no navegador de todos os visitantes.
Quando a JFrog analisou o repositório, ele já retornava erro 404, mas uma cópia arquivada de 30 de maio preservou o que havia sido servido: um flood HTTP rudimentar.
A cada 500 milissegundos, o script gera uma nova string com 1 milhão de caracteres e a envia como uma requisição POST no-cors para cdn.caan.edu, que a JFrog identifica como o domínio público de uma escola de enfermagem em Matteson, Illinois.
As requisições não aguardam resposta, então se acumulam.
A JFrog calcula que cada visitante ativo consome cerca de 2 MB por segundo de upload, o que significa que mil abas abertas do proxy poderiam empurrar algo em torno de 2 GB por segundo contra o alvo.
Um parâmetro de consulta aleatório dribla proxies de cache, e o no-cors dispensa a pré-verificação do CORS, evitando qualquer limitação dos pacotes.
O segundo módulo, I2, é o mais agressivo.
Ele busca um arquivo de texto simples, websocket.txt, que contém a URL de destino do WebSocket e uma contagem de sockets limitada entre 1 e 1.024, e então abre essa quantidade de conexões em um loop escalonado.
A configuração arquivada apontava cada navegador para 30 conexões em um endpoint Wisp em lunaron[.]top, que por sua vez era um proxy ativo e ocupado em injetar malvertising.
Wisp é um protocolo de baixo overhead da Mercury Workshop para encapsular muitos sockets TCP e UDP sobre um único WebSocket, e é uma base comum no mesmo ecossistema de proxy no navegador que esses pacotes imitam.
Depois de conectado, cada navegador coloca o socket em modo binário e, a cada 100 milissegundos, envia um frame CONNECT válido do Wisp seguido de um frame CLOSE, ambos apontados para localhost:1.
Os frames são pacotes Wisp corretos em little-endian, portanto o alvo não é a máquina do estudante.
É o servidor Wisp remoto na outra ponta da conexão.
Isso transforma o ataque em uma ação contra o plano de controle, e não em um ataque volumétrico.
Um único navegador executando os 1.024 sockets em plena carga pode levar um servidor Wisp a abrir e fechar cerca de 10.240 conexões por segundo, enquanto escreve mais de 20.000 linhas de log no mesmo intervalo.
A JFrog observa que o wisp-server-node da Mercury Workshop abre um novo socket para cada frame CONNECT sem verificar se o destino é um endereço de loopback ou privado, e registra cada tentativa.
Isso esgota descritores de arquivo, inunda o armazenamento de logs e derruba o proxy.
O wisp-server-node já está descontinuado, e seus mantenedores recomendam a migração justamente por esse tipo de problema de segurança e estabilidade.
Assim, a campanha transformou uma ferramenta de proxy estudantil em arma contra os servidores dos quais outros proxies estudantis dependem, e ainda direcionou um flood separado contra uma escola.
A infraestrutura é concentrada e não foi projetada para esconder sua origem.
A JFrog rastreou os builds até uma organização no GitHub chamada lucideproxy, cujas contas foram registradas com poucos segundos de diferença, vinculadas a um e-mail de commit em geeked[.]wtf e a um identificador no Discord.
Dos 93 hostnames de implantação encontrados, 90 resolviam para um único endereço IP, 92.38.177[.]17, hospedado pela G-Core Labs.
Entre os nomes infantis dos pacotes, um script shell de autopublicação deixado dentro dos tarballs e um comentário “TY WAVES + CHATGPT ILY” encontrado pela SafeDep no service worker, as duas empresas concluíram que o operador provavelmente é jovem.
Uma conta publicou 116 pacotes em menos de 35 minutos, e o npm não fez nada para desacelerar o processo.
O histórico de commits da JFrog mostra a evolução da campanha.
O projeto começou como simples adware em março, recebeu o carregador remoto e o gerador Wisp em uma explosão de dois dias em meados de maio, executou o flood contra a escola de enfermagem no fim do mês e, em 31 de maio, removeu os módulos maliciosos quando a cobertura jornalística começou.
Uma segunda leva, em 8 de julho, sob uma nova conta, elevou o total para 148 pacotes e publicou a versão limpa, apenas com adware.
O app continua ofuscado, ainda carrega scripts de terceiros vindos de domínios dos invasores, e o carregador segue apontando para uma branch mutável.
A capacidade de DDoS não desapareceu, apenas foi desligada.
A JFrog observa que os operadores mantêm a capacidade de reativá-la com um único commit nessa branch mutável, sem necessidade de atualizar o pacote.
Muitos dos pacotes da campanha já foram removidos do npm e substituídos pelo marcador padrão de segurança 0.0.1-security do registro.
Uma checagem pontual do The Hacker News, feita em 14 de julho de 2026, encontrou a maioria ausente, mas ainda havia charlie-kirk servindo as duas versões que a JFrog classificou como maliciosas, 2.0.0 e 3.0.1.
Como a ameaça chega como um app web do lado do cliente, e não como um implante que roda na instalação, a recomendação de resposta da JFrog acompanha esse modelo de entrega.
Administradores de redes escolares e corporativas, onde esses proxies concentram mais tráfego, devem bloquear os domínios da campanha no nível de DNS.
Os hosts de monetização e de scripts que a versão atual ainda acessa, entre eles woofbeginner[.]com e c.vipersfutbol[.]com, devem ser os primeiros a entrar na lista de bloqueio.
Quem abriu um desses sites proxy deve limpar o cache do navegador e o local storage, além de remover qualquer service worker deixado por um domínio de tutoria ou proxy.
Equipes cujos ambientes de build tenham buscado os pacotes citados devem retirá-los dos manifests e dos lockfiles e refazer a compilação a partir de uma base limpa.
O relatório da JFrog traz a lista completa dos 148 pacotes, domínios, endereços IP e hashes.
Scanners de dependências e sandboxes de instalação foram criados para detectar código que roda no npm install.
Este código nunca quis ser instalado.
Enquanto registros públicos continuarem servindo também como CDNs gratuitas, os pacotes mais preocupantes podem ser cada vez mais aqueles que nenhum pipeline de build jamais carrega.
Publicidade
Conheça a Solyd One, e tenha acesso a todos os cursos de segurança ofensiva da Solyd, todas as certificações práticas, incluindo a SYCP, 2 CTFs anuais com prêmios de até 30 mil reais, dezenas de laboratórios realísticos, suporte, comunidade e muito mais. Saiba mais...