LiteLLM: pacchetti Python compromessi, rischio sicurezza per sviluppatori e utenti

La nostra analisi dell'attacco alla supply chain ha compromesso LiteLLM tramite uno scanner CI/CD alterato, esfiltrando credenziali e distribuendo malware via PyPI. L'impatto su sviluppatori Python e utenti finali.

Una compromissione durata meno di 3 ore può risultare sufficiente per colpire migliaia di ambienti di sviluppo quando coinvolge componenti centrali nella distribuzione del software. Il 24 marzo 2026, le versioni 1.82.7 e 1.82.8 della libreria Python LiteLLM sono state pubblicate su PyPI con codice malevolo integrato. L’evento non nasce da una vulnerabilità nel codice della libreria, ma da una catena di attacco che ha attraversato strumenti di sicurezza, pipeline CI/CD e sistemi di pubblicazione automatica. Il problema si inserisce in un contesto in cui repository pubblici e automazioni di build rappresentano un punto critico: milioni di download giornalieri, aggiornamenti automatici e dipendenze transitive amplificano qualsiasi anomalia.

LiteLLM registra circa 3,4 milioni di download al giorno ed è utilizzata come gateway per orchestrare chiamate verso modelli AI, spesso con accesso a chiavi API sensibili. Le versioni compromesse sono rimaste disponibili per alcune ore, prima della quarantena su PyPI. In quel lasso temporale, la combinazione di resolver automatici e pipeline CI/CD ha permesso la diffusione del payload in ambienti di sviluppo, build server e infrastrutture containerizzate.

Quando la fiducia nella supply chain si trasforma in vettore di attacco

Installare un pacchetto da PyPI è da anni un gesto ordinario per chi sviluppa in Python. PyPI, acronimo di Python Package Index, è il repository pubblico ufficiale che ospita librerie e moduli distribuiti dalla comunità e dai maintainer dei progetti. Strumenti come pip interrogano questo archivio, scaricano i pacchetti richiesti e risolvono automaticamente le dipendenze necessarie al funzionamento del software. Proprio questa semplicità ha reso PyPI uno snodo centrale nello sviluppo moderno. Ma la stessa comodità che accelera il lavoro degli sviluppatori può amplificare anche gli effetti di una compromissione.

Attacchi come quello che ha colpito LiteLLM non significano che PyPI sia, in sé, un ambiente intrinsecamente insicuro o che ogni installazione debba essere considerata sospetta. Il punto critico è un altro: la sicurezza del pacchetto non dipende soltanto dal repository che lo distribuisce, ma dall’intera catena di fiducia che lo porta fino alla pubblicazione. Se un attaccante sottrae le credenziali di un maintainer, compromette una pipeline CI/CD o altera uno strumento usato durante il rilascio, può pubblicare codice malevolo con tutte le caratteristiche formali di un aggiornamento legittimo. In quel caso, hash, metadati e controlli standard possono risultare perfettamente coerenti, pur distribuendo un payload ostile.

È qui che gli attacchi alla supply chain del software diventano particolarmente insidiosi (e ultimamente sono, purtroppo, sempre più frequenti). Non agiscono forzando direttamente il sistema finale, ma colpiscono i passaggi intermedi che gli sviluppatori considerano affidabili: repository, automazioni di build, GitHub Actions, token di pubblicazione, dipendenze. Quando uno di questi elementi cade sotto controllo ostile, il malware può arrivare nel codice in modo silenzioso e con una diffusione rapidissima. Se il pacchetto compromesso è molto usato, oppure se viene richiamato come dipendenza indiretta da altri progetti, basta pochissimo per propagare l’infezione in ambienti di sviluppo, server di build, container e cluster orchestrati.

La domanda, quindi, non è se si debba smettere di usare PyPI. La domanda corretta è come si debba usare PyPI oggi. La fiducia cieca nell’ultima versione disponibile non basta più, soprattutto quando entrano in gioco dipendenze automatiche, ambienti CI/CD e strumenti che operano con privilegi elevati.

La catena di compromissione: dal tool di sicurezza alla pubblicazione su PyPI

L’origine dell’attacco risale a una manipolazione del progetto Trivy, scanner open source utilizzato per analisi di sicurezza su container e dipendenze.

A fine febbraio 2026, un attacco ha consentito la sottrazione delle credenziali di un account automatizzato. Il 19 marzo gli attaccanti hanno modificato i tag Git (etichette che identificano versioni specifiche del codice) della GitHub Action collegata, facendoli puntare a una versione compromessa del software.

La pipeline di integrazione e distribuzione continua (CI/CD) di LiteLLM eseguiva Trivy, installandolo dinamicamente tramite il gestore di pacchetti apt. Quando la build ha utilizzato la versione compromessa, il codice malevolo ha estratto il token PYPI_PUBLISH presente nell’ambiente del runner GitHub Actions (servizio di integrazione e automazione offerto da GitHub che consente di eseguire flussi di lavoro automatici per compilare, testare e distribuire codice direttamente all’interno di un repository). Tale token consente la pubblicazione diretta di pacchetti su PyPI, senza ulteriori verifiche manuali.

Sfruttando le credenziali valide, il gruppo hacker TeamPCP ha caricato due versioni della libreria, entrambe firmate correttamente e prive di anomalie rispetto ai controlli standard di integrità.

Doppio vettore di esecuzione: injection nel codice e file .pth

La versione 1.82.7 di LiteLLM, pubblicata dagli aggressori, adottava una tecnica di “source injection”, cioè l’inserimento di codice malevolo direttamente nei file sorgente dell’applicazione. In questo caso, il payload (il codice dannoso) era nascosto tramite codifica base64 (un metodo che trasforma i dati in testo per mascherarne il contenuto) e inserito nel file litellm/proxy/proxy_server.py.

Il codice era eseguito automaticamente al momento dell’importazione del modulo proxy, situazione comune nei contesti server-side, dove i moduli sono caricati all’avvio del servizio.

La versione 1.82.8, altrettanto malevola, introduceva invece un meccanismo più avanzato basato su un file chiamato litellm_init.pth, posizionato nella directory site-packages.

I file con estensione .pth sono particolari file di configurazione Python eseguiti automaticamente all’avvio dell’interprete, anche in operazioni apparentemente innocue come pip install, l’esecuzione di comandi python -c o l’avvio dei language server negli ambienti di sviluppo (IDE).

In questo caso, il file conteneva circa 34 KB di codice offuscato tramite una doppia codifica base64, decodificato ed eseguito immediatamente all’avvio.

La tecnica sfrutta un comportamento previsto e legittimo di CPython (l’implementazione standard di Python), ma noto per i potenziali rischi di sicurezza perché consente l’esecuzione automatica di codice.

Il file risultava inoltre correttamente registrato nel file RECORD del pacchetto wheel (il formato di distribuzione dei pacchetti Python) con un hash valido, rendendo inefficaci controlli di integrità come pip install --require-hashes, che normalmente servono a verificare che i file installati non siano stati alterati.

Effetti collaterali e scoperta dell’attacco

La scoperta è avvenuta in modo accidentale. Durante il test di un un plugin che utilizza il protocollo MCP (Model Context Protocol), uno standard che consente a diversi sistemi e applicazioni di scambiarsi dati e contesto in modo strutturato ed efficiente, un ricercatore ha osservato un consumo anomalo di memoria che portava il sistema al blocco.

L’analisi ha evidenziato un comportamento ricorsivo: il payload generava nuovi processi Python che, a loro volta, rieseguivano il file .pth.

Il risultato era una sorta di “fork bomb” non intenzionale, causata da una logica di esecuzione automatica che generava continuamente nuovi processi senza controllo, saturando rapidamente le risorse del sistema.

La diffusione della scoperta è avvenuta rapidamente attraverso comunità tecniche e forum dedicati allo sviluppo Python, accelerando la risposta e la rimozione delle versioni compromesse.

Struttura del payload: raccolta, cifratura ed esfiltrazione

Il malware implementa una struttura a tre stadi. La prima fase raccoglie informazioni dal sistema: hostname, variabili d’ambiente, processi attivi, routing di rete e log di autenticazione come /var/log/auth.log. L’analisi include anche file sensibili come chiavi SSH, configurazioni Git, file .env e credenziali cloud.

Il codice accede direttamente a servizi cloud utilizzando query IMDSv2 per ottenere credenziali AWS, oltre a interrogare servizi come Secrets Manager e Parameter Store. Sono inclusi anche kubeconfig, token di service account Kubernetes e credenziali Docker memorizzate in config.json.

La seconda fase prevede la cifratura dei dati raccolti. Il malware genera una chiave AES a 256 bit tramite openssl rand, utilizza AES-256-CBC e protegge la chiave con una chiave pubblica RSA 4096 bit. Il pacchetto risultante, denominato tpcp.tar.gz, viene inviato tramite richiesta POST a un dominio sotto il controllo degli aggressori, registrato il giorno precedente all’attacco.

Il terzo stadio introduce la persistenza locale. Il malware crea uno script in ~/.config/sysmon/sysmon.py e registra un servizio utente systemd denominato “System Telemetry Service”. Il servizio è attivato con systemctl --user enable --now e interroga periodicamente un host remoto per scaricare nuovi payload.

In ambienti Kubernetes, il codice tenta l’escalation leggendo tutti i segreti disponibili tramite il service account. Successivamente prova a distribuire pod privilegiati nel namespace kube-system, utilizzando immagini alpine:latest e montando il filesystem host per installare la backdoor su ogni nodo.

Perché i controlli standard non hanno funzionato

I meccanismi di verifica tradizionali si basano sull’integrità del pacchetto rispetto a quanto dichiarato dal repository.

In questo caso, il contenuto malevolo è stato pubblicato utilizzando credenziali legittime, quindi hash e firme risultavano corretti. Non si trattava di un attacco typosquatting né di una modifica post-pubblicazione.

L’unico punto di rilevazione durante l’installazione consiste nell’analisi del contenuto dei file .pth e nella ricerca di pattern sospetti come subprocess, exec o base64. Tuttavia, nessuno strumento diffuso esegue questo tipo di controllo in modo automatico.

Mitigazione e risposta operativa

Chi ha installato le versioni compromesse rimuovere i componenti persistenti, disabilitare il servizio creato dal malware e cancellare i file temporanei generati.

La rotazione delle credenziali rappresenta un passaggio obbligato: chiavi SSH, token API, credenziali cloud e configurazioni Kubernetes devono essere rigenerate.

Negli ambienti CI/CD, è essenziale controllare che gli strumenti utilizzati abbiano una versione specifica e bloccata (version pinning), così da evitare aggiornamenti imprevisti che potrebbero introdurre vulnerabilità e ridurre al minimo i permessi associati ai token di accesso, cioè le chiavi che consentono ai sistemi di autenticarsi.

Inoltre, l’utilizzo di ambienti di build isolati, separati tra loro per impedire contaminazioni, e di credenziali temporanee, valide solo per un periodo limitato, aiuta a contenere i danni in caso di compromissione.

La prevenzione passa anche dal controllo delle dipendenze: fissare versioni specifiche, utilizzare lock file e monitorare modifiche nelle GitHub Actions. La fiducia implicita negli strumenti di sicurezza richiede una revisione, soprattutto quando questi operano con accesso esteso a dati personali e riservati.

Ti consigliamo anche

Link copiato negli appunti