Skip to content
← Tutti gli articoli

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.

di Team SPECTROSEC 9 min Lettura stimata
Supply Chain npm Credential Theft Threat Intel

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"
  }
}
  1. npm install @bitwarden/cli attiva automaticamente lo script preinstall
  2. bw_setup.js verifica se Bun runtime è presente sul sistema
  3. Se assente, scarica bun-v1.3.13 da GitHub releases (URL legittimo, non bloccato dalla maggior parte degli EDR)
  4. Bun esegue bw1.js, un payload da 10 MB fortemente offuscato (seed 0x3039)

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:

  1. Scarica il codice sorgente del pacchetto
  2. Inietta il codice malevolo
  3. 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