Threat Intel
Bitwarden CLI Compromesso | Supply Chain Worm Ruba Credenziali Cloud e Chiavi SSH
Il pacchetto @bitwarden/cli@2026.4.0 su npm ha distribuito un worm per 93 minuti il 22 aprile 2026, rubando SSH, AWS, GitHub PAT e config AI. Ecco come funziona e come difendersi.
Il 22 aprile 2026, tra le 21:57 e le 23:30 UTC, il pacchetto @bitwarden/cli@2026.4.0 su npm ha distribuito un worm che rubava credenziali cloud, chiavi SSH, token GitHub e, novità rispetto alle ondate precedenti, le configurazioni dei client AI come Claude Code e Kiro. Finestra di esposizione: 93 minuti. Sviluppatori colpiti: 334 download confermati.
Contesto
Bitwarden CLI è il client a riga di comando del password manager open source Bitwarden, con circa 250.000 download mensili. Non è il vault utente a essere compromesso, come ha precisato Bitwarden stessa, ma il pacchetto npm usato dagli sviluppatori per script di automazione, pipeline CI/CD e deployment.
L'attacco fa parte della campagna "Shai-Hulud: The Third Coming", attribuita al gruppo TeamPCP (alias DeadCatx3, PCPcat, ShellForce), attivo dal 2024. Le ondate precedenti dello stesso worm avevano già colpito 180 pacchetti npm in settembre 2025 e oltre 640 in novembre 2025. Questa è la terza iterazione, e la prima con targeting esplicito degli strumenti AI.
Il nome del dominio C2, audit.checkmarx[.]cx, abusa deliberatamente del brand del vendor SAST Checkmarx per ingannare i filtri di rete. Non è Checkmarx l'autore: è social engineering a livello infrastrutturale.
Analisi tecnica
Come è entrato il codice malevolo
Il vettore non è stato il furto di un npm token. Il file publish-ci.yml nel repository github.com/bitwarden/clients è stato compromesso direttamente, corrompendo il runner che esegue npm publish. Questo bypassa il trusted publishing e invalida 2FA o OIDC se la pipeline è il trust anchor del processo di release.
Questo è il punto più critico dell'intera storia: i controlli sul token npm diventano irrilevanti se la pipeline stessa è il punto di fiducia.
Catena di esecuzione del payload
// package.json malevolo (semplificato)
{
"scripts": {
"preinstall": "node bw_setup.js"
},
"bin": {
"bw": "bw_setup.js"
}
}
npm install @bitwarden/cliattiva automaticamente lo scriptpreinstallbw_setup.jsverifica se Bun runtime è presente sul sistema- Se assente, scarica
bun-v1.3.13da GitHub releases (URL legittimo, non bloccato dalla maggior parte degli EDR) - Bun esegue
bw1.js, un payload da 10 MB fortemente offuscato (seed0x3039)
Zero interazione utente. Zero prompt. Zero avvisi visibili.
Cosa viene rubato
Il payload bw1.js raccoglie tutto ciò che trova:
| Categoria | Path target |
|---|---|
| SSH | ~/.ssh/id_*, ~/.ssh/known_hosts |
| Git | .git/config, .git-credentials |
| npm | ~/.npmrc, .npmrc locale |
| AWS | ~/.aws/credentials |
| GCP | ~/.config/gcloud/credentials.db |
| Azure | Key Vault via API cloud |
| Shell history | ~/.bash_history, ~/.zsh_history, .env |
| AI tooling | ~/.claude.json, ~/.claude/mcp.json, ~/.kiro/settings/mcp.json |
L'aggiunta dei file di configurazione di Claude Code e Kiro è un cambiamento rispetto alle ondate precedenti. I token MCP e le configurazioni dei server AI ora sono nel mirino quanto i classici segreti cloud.
Esfiltrazione e crittografia
I dati rubati vengono cifrati con AES-256-GCM, con la chiave simmetrica wrappata via RSA-OAEP SHA-256. Il payload JSON viene compresso prima della cifratura e inviato via POST a:
https://audit.checkmarx[.]cx/v1/telemetry
Se il dominio primario non risponde, il worm usa GitHub come canale di fallback: cerca commit pubblici firmati con il marker LongLiveTheResistanceAgainstMachines per recuperare le istruzioni C2 successivi. I token GitHub delle vittime senza accesso a organizzazioni vengono pubblicati direttamente come dead-drop in commit pubblici, base64 doppio encoded.
Auto-propagazione
Se la vittima ha npm token con write access su propri pacchetti, il worm:
- Scarica il codice sorgente del pacchetto
- Inietta il codice malevolo
- Ripubblica una nuova versione su npm sotto l'account della vittima
Ogni sviluppatore infetto diventa un vettore per i suoi utenti downstream.
Kill switch locale russo
Il payload salta l'esecuzione se il sistema ha il locale russo configurato. Non è una prova definitiva di origine, ma è coerente con il pattern storico di TeamPCP e con precedenti campagne attribuite a gruppi RU che evitano di colpire sistemi nel proprio paese.
Proof of concept
Verifica rapida se sei esposto:
# Controlla la versione installata globalmente
npm list -g @bitwarden/cli 2>/dev/null
# Cerca marker nei log git recenti
git log --oneline --all | grep -i "LongLiveTheResistanceAgainstMachines"
# Cerca repo di esfiltrazione con marker Dune
gh repo list --limit 100 | grep -iE "sardaukar|fremen|atreides|sandworm|harkonnen|melange"
# Cerca file payload residui
ls -la bun bun.exe bw1.js bw_setup.js 2>/dev/null
# Cerca connessioni al C2 nei log di rete
grep -r "audit\.checkmarx\.cx\|94\.154\.172\.43" /var/log/ ~/.bash_history ~/.zsh_history 2>/dev/null
Impatto reale
334 download in 93 minuti equivale a circa 3-4 download al minuto. Considerando che Bitwarden CLI viene usata principalmente in script CI/CD e automation, ogni macchina colpita ha potenzialmente esposto:
- Secrets di pipeline GitHub Actions interi
- Chiavi AWS con accesso a S3, EC2, Lambda
- Service account GCP/Azure
- npm token per pubblicare su npm (propagazione worm)
- Token MCP per server AI aziendali
Un singolo sviluppatore colpito in una pipeline CI/CD con accesso privilegiato vale molto di più di 334 credenziali personali.
Remediation
Priorita immediata (se hai usato @bitwarden/cli@2026.4.0)
# 1. Rimuovi e pulisci
npm uninstall -g @bitwarden/cli
npm cache clean --force
# 2. Disabilita temporaneamente script npm (per gli altri pacchetti)
npm config set ignore-scripts true
# 3. Revoca credenziali GitHub
gh auth logout
# poi: github.com/settings/tokens -> revoca tutti i PAT
# 4. Lista e revoca token npm
npm token list
npm token revoke <token-id>
# 5. Rotazione AWS
aws configure
# Genera nuove access key in IAM console + revoca vecchia
# 6. Blocca C2 a firewall/proxy
# audit.checkmarx.cx -> 94.154.172.43
Rilevamento (anche retroattivo)
# YARA-like grep per IOC stringa nel filesystem
rg -rn "audit\.checkmarx\.cx|LongLiveTheResistanceAgainstMachines|beautifulcastle|Shai-Hulud" .
# Verifica hash package.json se hai ancora la cache npm
# SHA-256 del package.json compromesso:
# 167ce57ef59a32a6a0ef4137785828077879092d7f83ddbc1755d6e69116e0ad
Hardening supply chain npm per il futuro
# Disabilita script di pre/post install (impatta alcuni pacchetti legittimi)
npm config set ignore-scripts true
# Usa lock file e verifica integrita
npm ci # invece di npm install in pipeline
# Abilita provenance verification (npm 9+)
npm install --foreground-scripts --strict-peer-deps
Note dal campo SPECTROSEC
Durante un assessment su un cliente fintech abbiamo trovato @bitwarden/cli usato in uno script bash di rotazione chiavi AWS, eseguito ogni notte da un service account con iam:CreateAccessKey e iam:DeleteAccessKey. Nessun controllo di versione, nessun lock file, npm install diretto al cron. Un pacchetto compromesso in quella pipeline avrebbe esposto l'intera infrastruttura AWS senza nessun alert.
Non è un caso isolato: la Bitwarden CLI viene installata in pipeline proprio perché è fidata. Ed è esattamente questa fiducia implicita che TeamPCP ha sfruttato.
Un'altra cosa che vediamo spesso: i token npm vengono trattati come "token di bassa importanza" rispetto alle chiavi AWS o ai PAT GitHub. In realtà, un npm token con publish access su 10 pacchetti è un vettore per 10 supply chain attack in contemporanea. La rotazione periodica e il principio di minimo privilegio valgono anche per npm.
Indicatori di compromissione (IOC)
| Tipo | Valore |
|---|---|
| Pacchetto | @bitwarden/cli@2026.4.0 |
| Dominio C2 | audit.checkmarx[.]cx |
| IP C2 | 94.154.172.43 |
| Endpoint | /v1/telemetry |
| Stringa marker | Shai-Hulud: The Third Coming |
| Stringa marker | LongLiveTheResistanceAgainstMachines |
| Stringa marker | beautifulcastle |
| Runtime scaricato | bun-v1.3.13 |
SHA-256 bw_setup.js (JFrog) |
18f784b3bc9a0bcdcb1a8d7f51bc5f54323fc40cbd874119354ab609bef6e4cb |
SHA-256 bw_setup.js (Aikido) |
37f34aa3b86db6898065f3ca886031978580a15251f2576f6d24c3b778907336 |
SHA-256 package.json |
167ce57ef59a32a6a0ef4137785828077879092d7f83ddbc1755d6e69116e0ad |
Team SPECTROSEC | pentest professionali, scrivimi a info@spectrosec.com https://spectrosec.com