Blog
Supply ChainnpmeBPFTrusted Publishing

IronWorm npm: quando il Trusted Publishing diventa la superficie d'attacco

JFrog ha scoperto IronWorm il 3 giugno: worm npm con binario Rust, rootkit eBPF, C2 su Tor e auto-propagazione via token OIDC di npm Trusted Publishing.

Zero Hunt Research··8 min di lettura

Da due anni la risposta industry-standard agli attacchi supply-chain in ecosistema npm è la stessa: ruotate i token, abilitate la 2FA, pubblicate dalle vostre GitHub Actions tramite npm Trusted Publishing, il flusso che scambia un identity token OIDC del runner CI con una credenziale di pubblicazione scoped al singolo package e con TTL di pochi secondi. Niente token long-lived su disco, niente token nelle variabili d'ambiente, niente da rubare.

Il 3 giugno 2026 JFrog Security Research ha pubblicato l'analisi di IronWorm, un worm npm che usa proprio il flusso OIDC del Trusted Publishing come meccanismo di auto-propagazione. Il consiglio di ruotare i token resta valido. Solo che non tocca più il percorso che l'attaccante in pratica usa.

IronWorm in numeri

  • 36 package npm trojanizzati distribuiti su 9 organizzazioni GitHub — fra cui ocrybit, asteroid-dao, alisista, warashibe, kakedashi-hacker, weavedb, ArweaveOasis, arthursimao, mlebjerg (JFrog).
  • 57 commit malevoli retrodatati, firmati con l'identità generica claude, scelta — scrive JFrog — per confondersi con il rumore di dependabot, renovate e github-actions.
  • ~32.177 download mensili / 148.724 download lifetime complessivi sui package coinvolti, secondo il tracker di OX Security.
  • Il punto d'ingresso è stato l'account del mantainer asteroiddao; il bersaglio visibile è l'ecosistema Web3 Arweave (WeaveDB, AO, Arweave Oasis), con SlowMist che ha emesso l'allerta lato Web3.
  • A circa 24 ore dalla scoperta di JFrog le versioni malevole sono state deprecate dallo stesso operatore — rimozione silenziosa, non un takedown del registry.

L'eco mainstream è arrivata il 4 giugno con il pezzo di BleepingComputer. In parallelo, Dark Reading ha segnalato che StepSecurity ed Endor Labs stavano tracciando una campagna npm non correlata ma contemporanea (binding.gyp): due attori distinti che facevano girare due worm in ecosistema nella stessa settimana.

Il gap lato host: un ELF Rust con rootkit eBPF

La quasi totalità dei payload supply-chain JavaScript è JavaScript: uno script post-install, un downloader Node minimale, magari un blob base64. Sono cose che un'analisi statica del tarball del package vede.

IronWorm no. Lo preinstall hook del package malevolo droppa ed esegue un ELF Rust da 976 KB — solo Linux — confezionato con uno stub UPX custom in cui i magic bytes UPX sono stati sovrascritti per neutralizzare gli unpacker signature-based. Una volta in esecuzione, carica un modulo kernel eBPF che nasconde il proprio processo a ps / top, i propri file a ls, e i propri socket a netstat filtrando le risposte netlink. L'anti-debug è integrato: ogni ptrace contro il processo protetto risponde con SIGKILL. Ogni stringa del binario è cifrata con parametri specifici per il call-site, quindi un dump di memoria non vi restituisce una lista di IoC in chiaro.

Due scelte di design contano per chi deve difendere.

Primo: preinstall parte prima della risoluzione delle dipendenze. Non c'è finestra per una policy "sandbox-on-install" che ispezioni l'albero del package prima dell'esecuzione del codice — il codice gira come prima cosa che npm fa dopo il fetch.

Secondo: un rootkit eBPF kernel-level è invisibile ai processi userland di cui gli EDR sono fatti. La lista degli hook che l'EDR enumera non è la lista che il kernel sta in realtà onorando. Le visibilità endpoint standard — process tree, file open, socket creation — sono esattamente ciò che il rootkit è progettato per oscurare.

Non è teorico. Una volta caricato il rootkit, l'host che monta l'EDR appare pulito.

C2 su Tor e il pivot via Trusted Publishing

L'implant beacona su Tor verso un endpoint onion /api/agent. Chi guarda il traffico vede: un host che storicamente parla solo con registry.npmjs.org, github.com e pypi.org sta improvvisamente mantenendo una sessione cifrata long-lived verso un entry guard Tor. Quello è il segnale.

Ma il pivot più interessante è come IronWorm si propaga alla prossima vittima. L'implant raccoglie l'identity token OIDC dell'ambiente CI e lo invia a:

https://registry.npmjs.org/-/npm/v1/oidc/token/exchange/package/<pkg>

Il token exchange restituisce un token di automazione scoped al package, con TTL breve. Il malware pubblica allora la versione successiva trojanizzata di ogni package per cui il runner CI compromesso ha diritti di pubblicazione. Dal punto di vista di npm, la publish è completamente legittima: arriva dal Trusted Publisher configurato, firmata con un token che npm stesso ha appena emesso.

"Abbiamo ruotato tutti i token npm il trimestre scorso." — bene. La publish IronWorm non ha usato un token salvato. Ha usato uno scambio OIDC durato ~30 secondi dentro la vostra GitHub Actions runner, verso l'endpoint che npm vi dice di usare, con un'identità di cui npm si fida perché il vostro repository l'ha configurata come Trusted Publisher.

È il cambiamento strutturale che Microsoft ha segnalato il 2 giugno nel writeup parallelo su Red Hat Miasma: il percorso OIDC-exchange è oggi una superficie di propagazione attiva nell'ecosistema npm, non più un modello di rischio ipotetico.

Cosa esfiltra: chiavi LLM e config degli AI coding agent

IronWorm esfiltra 86 variabili d'ambiente e 20 percorsi di file credenziali. La lista delle env-var è la parte interessante — include 14 chiavi di provider LLM: Anthropic, OpenAI, Gemini, Cohere, Mistral, Groq, Perplexity, xAI — accanto ai blocchi consueti AWS / GCP / Azure / cloud-storage / Vault / Kubernetes / npm / SCM / messaging.

I percorsi di file includono quelli standard (~/.aws/credentials, ~/.ssh/id_*, ~/.docker/config.json, ~/.kube/config, ~/.vault/, le directory dei wallet Exodus) e un sottoinsieme meno standard: le directory di configurazione di Claude, Codex, Cursor e Gemini. Gli AI coding tools salvano localmente API key, configurazioni dei server MCP, e in certi casi contesto di progetto. IronWorm è il primo worm npm largamente tracciato a mettere quei file nella propria target list — e difficilmente sarà l'ultimo.

Per un'organizzazione di ingegneria che nel 2025 ha portato gli AI coding tools dentro le workstation degli sviluppatori, il pacchetto di esfiltrazione IronWorm è anche il blast-radius realistico di una compromissione: ogni provider LLM che usate, ogni token CI, ogni credenziale cloud.

Perché la difesa solo-host non basta

Mettiamo le righe in fila:

Strato difensivo Cosa vede durante un'infezione IronWorm attiva
Analisi statica del package all'install Uno preinstall che esegue un ELF Linux. Sospetto, ma il corpo dell'ELF è UPX-mangled e con stringhe cifrate; signature / yara funzionano male.
EDR endpoint Un host pulito. Il rootkit eBPF nasconde processi, file e socket. L'agent vede ciò che il rootkit decide di mostrargli.
Vulnerability scanner Niente — i package coinvolti non sono vulnerabilità CVE, sono versioni pubblicate, fidate, firmate di dipendenze che già usate.
Verifica firma di registry Pass. La publish è passata da Trusted Publishing OIDC. La firma è valida.
Monitoraggio egress di rete Una nuova sessione Tor long-lived da un host che storicamente parlava solo con registry di package e SCM.
DNS lineage Lo script preinstall ha innescato le prime lookup DNS verso entry node Tor mai osservate su quel build node.

L'unica riga che rileva un'infezione IronWorm in corso è la rete. L'analisi statica prende il package se il vostro unpacker è fortunato. La verifica della trust chain è strutturalmente bypassata. L'EDR non vede nulla. L'esfiltrazione passa per un tunnel cifrato da un build runner verso un onion service.

Questa è la motivazione operativa per trattare il comportamento di egress di rete come una superficie di rilevamento di prima classe — non un campo di arricchimento del SIEM, non una riga di log che si legge la mattina dopo. Il segnale esiste a wire-speed; la domanda è se qualcosa lo sta guardando a wire-speed.

Come Zero Hunt affronta questo scenario

Il pillar AI Traffic Analysis di Zero Hunt è un modello deep learning proprietario, addestrato su miliardi di sequenze PCAP, con quattro inference head in parallelo — classificazione del traffico sospetto, classificazione malware, identificazione del tipo d'attacco, application fingerprinting — eseguito localmente sulla GPU dell'appliance a una baseline di 2,7+ Gbit/s. Per la classe IronWorm gli output rilevanti sono il primo e il quarto: identificare che un build host sta sostenendo una sessione cifrata in uscita il cui profilo di timing e dimensione pacchetti corrisponde a Tor, e riconoscere che l'applicazione che la sta originando non appartiene ad alcuna categoria che quell'host abbia mai parlato.

Il rilevamento avviene mentre l'esfiltrazione è in corso, non dopo un digest SIEM. Funziona indipendentemente dal fatto che l'EDR sull'host veda o non veda il processo — e nel caso IronWorm l'EDR non lo vede affatto.

Il lavoro complementare avviene sul Pillar 1. Il corpus AI Gym che sostiene lo swarm generativo di pentest di Zero Hunt include skill offensive auto-evolutive sui percorsi di compromissione supply-chain — il motore a 10 agenti può comporre una campagna di pentest che esercita la pipeline CI del cliente contro abuse di preinstall hook, misuso del flusso OIDC di Trusted Publishing, e percorsi di credenziali sulle workstation degli sviluppatori, validata contro il corpus di backtest di AI Gym (142+ skill, i 314 task black-box CVE-based di Vulhub-Bench, i 200 task CSAW di NYU CTF Bench) prima che una qualunque nuova skill tocchi un ambiente di produzione. Ogni finding è firmato ECDSA al momento della scrittura e mappato sui 32 framework di compliance che Zero Hunt traccia, in modo che la audit-trail di "cosa abbiamo testato contro minacce classe IronWorm questo trimestre, e cosa abbiamo trovato" esista per costruzione.

E una postilla sull'operatore di IronWorm, perché il finale umano è troppo bello per non citarlo: secondo JFrog, la routine di esfiltrazione dell'implant porta in chiaro nella propria skip-list una seed phrase BIP-39"bench crane defense corn wheel trial…" — perché in fase di test il malware non rubasse il wallet dell'operatore stesso. È, ovviamente, l'unico punto del binario in cui una BIP-39 compare in chiaro. JFrog sintetizza: "l'operatore ha spedito la propria seed phrase a tutti."

La lezione per chi difende è più sobria. La risposta supply-chain — Trusted Publishing — funziona come previsto. È il blast-radius del design che è appena stato riscritto.