Blog
Supply ChainSLSA ProvenanceSicurezza CI/CDMalware Firmato

Firmato non vuol dire sicuro: quando la provenance SLSA distribuisce malware

Mini Shai-Hulud ha pubblicato pacchetti npm con provenance SLSA Build Level 3 valida e firma Sigstore. La fiducia nella supply chain si è rotta un livello più sotto — e il traffico runtime è l'ultima linea che ancora lo vede.

Zero Hunt Research··9 min di lettura

La firma era valida. Il pacchetto era malware.

L'11 maggio 2026 UTC una nuova ondata del worm Shai-Hulud ha colpito npm. Il giorno dopo, quando BleepingComputer ha pubblicato la sua analisi, i ricercatori contavano 84 versioni malevole distribuite su 42 pacchetti @tanstack/*, più @mistralai/*, Guardrails AI, UiPath, OpenSearch, Bitwarden CLI e diversi moduli pubblicati da SAP. Endor Labs ha calcolato oltre 160 pacchetti compromessi solo su npm; Socket ha contato 416 artefatti malevoli tra npm e PyPI. Il threat actor è tracciato come TeamPCP.

Ciò che distingue questo incidente da ogni worm npm precedente non è il volume. È quello che i pacchetti portavano nei propri metadati: attestazioni di provenance SLSA Build Level 3 valide e firma Sigstore, generate dai workflow upstream legittimi in esecuzione su runner GitHub Actions dirottati. La catena crittografica ha verificato tutto. A valle — npm install, gate di provenance a livello di organizzazione, CLI Sigstore, qualsiasi consumer che legga l'attestazione in-toto — è arrivato il bollino "trusted publisher" e i byte sono passati. Il dropper è partito al primo install.

Se gli ultimi tre anni di investimento sulla supply chain si sono fermati alla posizione "verifica la firma, fidati del pacchetto", questo è l'incidente che manda in pensione quella posizione.

Perché rompe il modello difensivo

I controlli sulla supply chain del software — SLSA, in-toto, Sigstore, l'attestazione nativa di GitHub — sono stati progettati attorno a un solo threat model: un attaccante ruba il token di publish del maintainer, o riesce a far passare malware non firmato attraverso un gate. La contromisura è legare il processo di build a un'identità di firma verificabile, così i consumer a valle possono rifiutare tutto ciò che non è firmato da quell'identità.

Il modello assume che il firmatario sia onesto. In un attacco Pwn Request / pull_request_target il firmatario è onesto — ma il firmatario è anche un build runner che esegue codice inviato da un contributor esterno, con pieno accesso al token OIDC e al materiale di firma. L'attaccante non deve rubare nulla. Costringe il firmatario legittimo a firmare il suo codice.

L'effetto a valle è scomodo ma chiaro:

  • La provenance verificabile mantiene valore — ma per attribuzione, non per fiducia. A posteriori puoi dimostrare quale workflow ha prodotto quale artefatto. Non puoi dimostrare che quel workflow non sia stato costretto a produrre malware.
  • Un pacchetto firmato non è più un segnale di sicurezza più forte di uno non firmato contro questa classe di attacco. Sembra solo che lo sia.
  • I gate basati solo sulla provenance ("blocca tutto ciò che non è SLSA L3") non aggiungono protezione contro il malware firmato. Il pacchetto ha SLSA L3.

"Non paghiamo perché non trattiamo con i criminali." — Grafana, sul rifiuto del tentativo di estorsione di CoinbaseCartel dopo che la stessa classe di abuso OIDC/Action ha portato al furto completo del codebase, comunicato il 2026-05-16.

L'incidente Grafana non è direttamente collegato a TeamPCP, ma è lo stesso pattern operativo abusato da un gruppo diverso nella stessa settimana. Quando due campagne non correlate sfruttano la stessa classe di abuso GitHub Actions contro vittime di alto profilo nell'arco di sette giorni, il pattern — non il singolo progetto — è la notizia.

Lo schema OIDC / Pwn Request

La catena tecnica dentro Mini Shai-Hulud ha combinato tre classi di vulnerabilità note ma raramente concatenate sui runner GitHub Actions. Il reverse engineering di Snyk e l'analisi di Wiz convergono sui tre passi:

  1. Workflow pull_request_target rischioso. A differenza di pull_request, pull_request_target esegue il workflow nel contesto del repo target — con i suoi secret e i suoi permessi OIDC — anche quando la PR che lo innesca arriva da un fork non fidato. Molti progetti lo accettano per ragioni legittime (auto-labeling, controllo CLA). Molti dimenticano anche che la PR può modificare proprio gli script che il workflow esegue.
  2. Cache poisoning di GitHub Actions. L'attaccante deposita artefatti controllati nella cache di Actions tramite la PR dal fork. All'esecuzione successiva di un workflow privilegiato, quegli artefatti vengono ripristinati nel runner ed eseguiti.
  3. Furto del token OIDC dalla memoria del runner. Con esecuzione di codice dentro il runner privilegiato, l'attaccante estrae dall'ambiente il token OIDC short-lived usato per autenticarsi all'endpoint di trusted publisher di npm. Il token è valido. La chiamata di publish è firmata. La provenance SLSA viene generata. Sigstore firma il risultato.

Niente in questa catena richiede secret long-lived rubati. Le credenziali npm del maintainer, le chiavi SSH, la root Sigstore — tutto intatto. L'attaccante ha sfruttato il confine di fiducia tra runner e publisher, che è esattamente il confine che lo stack moderno della supply chain era stato progettato per rendere affidabile.

Cosa ha fatto davvero il post-install

Una volta che un @tanstack/[email protected] malevolo è atterrato su una macchina di sviluppo, il payload è stato eseguito dentro il ciclo di vita di npm install. Dal postmortem di TanStack e dall'analisi corroborante dei vendor, il dropper:

  • Ha preso di mira workstation di sviluppo macOS e Linux (i path Windows non compaiono nel loader)
  • Ha raccolto chiavi SSH da ~/.ssh, credenziali cloud da ~/.aws, ~/.config/gcloud e dalla cache della Azure CLI
  • Ha estratto cookie di sessione del browser per GitHub e npm
  • Ha effettuato beacon sulla rete crittografata peer-to-peer di Session, scelta deliberatamente perché il traffico C2 si confonde con quello di un client Session normale ed è difficile da bloccare a livello firewall o DNS
  • Conteneva logica di geofencing: payload secondari distruttivi mirati a endpoint in Russia, Israele e Iran

OpenAI ha confermato pubblicamente il 14 maggio 2026 che due dev device macOS di dipendenti sono stati compromessi. Gli attaccanti hanno condotto "attività di esfiltrazione mirata alle credenziali" verso i repository interni raggiungibili da quelle macchine. OpenAI ha ruotato il certificato di firma della propria desktop app macOS, fissato al 12 giugno 2026 la revoca definitiva del certificato precedente, e dichiarato che i dispositivi colpiti erano ancora in un rollout graduale di nuovi controlli di package manager — minimumReleaseAge e validazione della provenance che avrebbero bloccato l'install. Le due macchine compromesse non avevano ancora ricevuto quella configurazione.

Quel dettaglio conta. Le mitigation esistevano. Il deployment non era ancora finito. Una finestra di nove giorni tra "conosciamo il controllo" e "tutti gli endpoint hanno il controllo" è stata sufficiente per centrare una vittima di alto profilo.

La detection si sposta sulla rete

In quale strato del lifecycle questo attacco diventa visibile?

Strato difensivo Vede l'attacco? Perché / Perché no
Gate di provenance SLSA No La provenance è valida, firmata dal workflow legittimo
Verifica Sigstore No La firma risale a un'identità Fulcio fidata
npm audit No Il pacchetto è troppo recente per essere in un advisory
EDR sulla dev workstation Parziale, in ritardo Vede node fare spawn di un processo figlio. Legge "npm che fa cose npm"
Allow-list DNS Parziale Sconfigge i C2 naïve; non vede Session P2P
ML sul traffico a wire-speed Vede destinazioni inedite + burst outbound + shape del protocollo

Il segnale c'è. Solo, non sta negli strati su cui tutti hanno investito. L'install del pacchetto avvia, in un minuto strettamente clusterizzato, un comportamento che la workstation non ha mai prodotto prima: centinaia di letture piccole su ~/.ssh e ~/.aws, un albero di processi che parte da npm e contatta nodi di bootstrap Session che la rete aziendale non ha mai visto, e una distribuzione di dimensione del payload outbound che non coincide con il traffico normale dello sviluppatore. Nessuno di questi segnali è di per sé ad alta confidenza. Insieme, su una workstation che storicamente solo riceve traffico, sono un evento.

Questo è lo strato dove un difensore ha ancora una possibilità contro il malware firmato. La catena crittografica ha regalato all'attaccante un pass libero per i gate di verifica statici. Il traffico runtime no.

Cosa significa "rispondere bene"

Azioni concrete per i team di security che stanno rivedendo la propria esposizione a questa classe di attacco:

  • Fai audit di ogni workflow pull_request_target nella tua org. Qualsiasi workflow che combini pull_request_target con il checkout della HEAD della PR è una issue critica. O pinni a uno SHA fisso dentro il file di workflow, o migri a pull_request e a un job di publish separato e gated.
  • Limita il blast radius dei token OIDC. Usa lo scope id-token: write più stretto possibile. Se il workflow non pubblica, non concedere il permesso.
  • Imponi minimumReleaseAge su npm e pip. Una soglia minima di 72 ore avrebbe intercettato Mini Shai-Hulud prima che le versioni colpite raggiungessero la maggior parte degli install. È il controllo a maggiore leva per organizzazioni che hanno già una CI solida.
  • Pin dei lockfile per integrity hash. Assumi che verranno aggirati di nuovo — ma il costo per bypassarli non è zero e rallenta gli attaccanti.
  • Tratta le dev workstation come punti di egress monitorati. Non sono più "endpoint trusted dietro il proxy". La baseline di egress di una macchina di sviluppo è stretta e osservabile; le deviazioni da quella baseline sono il segnale.
  • Mantieni una allow-list runtime di destinazioni outbound per workload. Tutto il resto è un evento. I nodi di bootstrap Session P2P sono una cosa particolarmente economica da baseline-are — quasi nessun traffico aziendale legittimo li tocca.

Nessuna di queste azioni dipende dal sapere in anticipo che un certo pacchetto è malevolo. È il punto. La prossima campagna di malware firmato userà un pacchetto diverso e un canale di esfiltrazione diverso.

Dove si colloca Zero Hunt

Quasi tutto questo articolo è enunciato del problema. Dove si colloca la piattaforma è più stretto e specifico.

Il pilastro che conta qui è l'AI Traffic Analysis. Un pacchetto npm firmato è invisibile ai gate di provenance per costruzione — la firma è il gate. Quello che non è invisibile è il dropper che contatta i nodi di bootstrap Session, l'harvest di credenziali SSH/cloud che esce in rete come burst outbound, e il fan-out laterale dopo che il primo beacon atterra su una credenziale. Il modello deep-learning di Zero Hunt esegue quattro teste di inferenza parallele — traffico sospetto, classificazione malware, identificazione del tipo di attacco, fingerprint applicativo — sullo stream PCAP live a 2.7+ Gbit/s di throughput baseline su una singola appliance. È addestrato su miliardi di sequenze e gira localmente sulla GPU dell'appliance; niente in quel percorso di detection dipende dal cloud, dal fatto che il pacchetto sia in una lista di noti-cattivi, o dal fatto che la firma sia rotta. Il comportamento è il segnale.

Il pilastro secondario è l'AI Generative Pentest. Lo swarm a 10 agenti può modellare il blast radius post-compromissione a partire da una dev workstation dirottata — che cosa raggiunge un attaccante da un singolo foothold, quali repository interni e ruoli cloud sono esposti via le credenziali raccolte, dove termina il movimento laterale. L'output è una catena di finding, firmata ECDSA al momento della scrittura, che sopravvive alla conversazione di audit che arriva sempre dopo un incidente di questa classe.

Il motivo per cui gli incidenti di supply chain si leggono così male nei postmortem è la distanza tra "ci siamo fidati della firma" e "abbiamo visto il C2 nove giorni dopo". La posizione difendibile non è fidarsi di meno; è guardare lo strato che la firma non può garantire. Quello strato è il filo.

Fonti: