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.
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:
- Workflow
pull_request_targetrischioso. A differenza dipull_request,pull_request_targetesegue 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. - 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.
- 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/gcloude 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 | Sì | 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_targetnella tua org. Qualsiasi workflow che combinipull_request_targetcon il checkout della HEAD della PR è una issue critica. O pinni a uno SHA fisso dentro il file di workflow, o migri apull_requeste a un job di publish separato e gated. - Limita il blast radius dei token OIDC. Usa lo scope
id-token: writepiù stretto possibile. Se il workflow non pubblica, non concedere il permesso. - Imponi
minimumReleaseAgesu 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: