
Divulgazione dettagliata di CVE-2025-68664, una vulnerabilità critica di deserializzazione nel core di LangChain che consente l'esfiltrazione di segreti e potenziale RCE attraverso prompt e flussi di serializzazione appositamente creati.
Autore: Yarden Porat
Ricerca Cyata: Vulnerabilità LangGrinch in LangChain
Pubblicato: https://cyata.ai/blog/langgrinch-langchain-core-cve-2025-68664/

Ieri LangChain ha pubblicato un avviso critico su una vulnerabilità che ho scoperto in langchain-core: CVE-2025-68664 / GHSA-c67j-w6g6-q2cm.
All'inizio di quest'anno, la mia ricerca si è concentrata sull'elusione dei gestori di segreti nel nostro lavoro "Vault Fault" – sistemi progettati specificamente come confine di sicurezza attorno ai tuoi dati più sensibili. Una conclusione si è ripetuta più e più volte: quando una piattaforma elabora accidentalmente dati modellati da un attaccante come una struttura affidabile, quel confine crolla rapidamente. Questa volta il sistema che "si rompe" non è il tuo gestore di segreti. È il framework degli agenti che potrebbe usarli.
Perché questa vulnerabilità merita particolare attenzione:
È nel core. Non è un bug di uno strumento specifico, né un caso limite di integrazione, né "un pacchetto della comunità che ha fatto qualcosa di strano." Le API vulnerabili (dumps() / dumpd()) sono nel cuore di langchain-core.
Il raggio d'azione è enorme. Per volume di download, langchain è uno dei componenti del framework AI più ampiamente distribuiti al mondo oggi. A fine dicembre 2025, la telemetria pubblica dei pacchetti mostra centinaia di milioni di installazioni, con pepy.tech che riporta ~847 milioni di download totali e pypistats che mostra ~98 milioni di download nell'ultimo mese.
Un singolo prompt può innescare molti meccanismi. Il percorso reale più comune qui non è "un attaccante ti invia un blob serializzato e tu chiami load()." È più sottile: gli output degli LLM possono influenzare campi come additional_kwargs o response_metadata, e questi campi possono essere serializzati e poi deserializzati attraverso normali funzioni del framework come lo streaming di log/eventi. In parole povere, ciò significa che l'exploit può essere innescato da un singolo prompt testuale che si propaga in un pipeline interno inaspettatamente complesso.
Prima di continuare a leggere, le patch sono già state rilasciate nelle versioni 1.2.5 e 0.3.81. Se stai usando LangChain in produzione, è più complicato di quanto possa sembrare; per favore, aggiorna il prima possibile.
LangChain utilizza un formato di serializzazione interno speciale in cui i dizionari contenenti il marcatore 'lc' rappresentano oggetti LangChain. La vulnerabilità era che dumps() e dumpd() non escapavano correttamente i dizionari controllati dall'utente che includevano accidentalmente la chiave riservata 'lc'.
Così, non appena un attaccante riesce a far serializzare e poi deserializzare il ciclo di orchestrazione di LangChain un contenuto che include la chiave 'lc', può istanziare un oggetto arbitrario non sicuro, potenzialmente innescando molti percorsi favorevoli all'attaccante.
L'avviso elenca 12 diversi flussi vulnerabili che sono estremamente comuni in casi d'uso reali, come lo streaming di eventi standard, la registrazione, la cronologia dei messaggi/memoria o la cache:

Le conseguenze più devastanti includono:
Estrazione di segreti dalle variabili d'ambiente. L'avviso nota che ciò si verifica durante la deserializzazione con secrets_from_env=True. Degno di nota, questo era il default fino a ieri. 🙂
Istanziamento di oggetti in namespace pre-approvati (inclusi langchain_core, langchain_openai, langchain_aws, langchain_anthropic…), potenzialmente causando effetti collaterali nei costruttori (chiamate di rete, operazioni su file, ecc.).
In determinate condizioni, l'istanziamento di oggetti LangChain può portare all'esecuzione arbitraria di codice.
Questo è classificato sotto CWE-502: Deserializzazione di dati non attendibili, con un punteggio CVSS CNA 9.3 (Critico).
Alla vigilia di Natale, stavo facendo il lavoro meno festivo: guardavo il codice di serializzazione e mi chiedevo "aspetta... perché questo è considerato attendibile?"
La ricerca sulla sicurezza spesso sembra drammatica dall'esterno. In realtà, di solito è lettura attenta, piccole ipotesi e lenta accumulazione di momenti "questo è strano".
Tutto è iniziato come molti in Cyata: con una semplice domanda che ci poniamo costantemente quando valutiamo gli stack AI per il rischio reale:
Dove sono i confini di fiducia nelle applicazioni AI e gli sviluppatori sanno davvero dove si trovano?
LangChain è un framework potente e, come la maggior parte dei framework moderni, deve spostare dati strutturati complessi: messaggi, chiamate a strumenti, eventi di streaming, tracce, cache e 'runnable'.
Rivedendo ricerche precedenti, c'era già un'ampia ricerca sugli strumenti e le integrazioni di LangChain, ma pochissime scoperte nella libreria principale.
Ho iniziato la ricerca lavorando all'indietro. Trovare punti interessanti (sink), poi capire come un attaccante potrebbe raggiungerli. La deserializzazione era un obiettivo ovvio.
Ci ho messo parecchio tempo per trovare qualcosa di significativo. Ma dopo un po' ho scoperto che, assumendo un primitivo di deserializzazione controllato dall'attaccante, potevo innescare un SSRF cieco che poteva essere usato per esfiltrare variabili d'ambiente (sarà descritto in dettaglio a breve). Poiché il risultato era limitato all'esfiltrazione di segreti e non al mio obiettivo principale di RCE, ho continuato a fare audit sulla deserializzazione e mi sono preso il mio tempo.
Il bug non era un pezzo di codice cattivo, era l'assenza di codice. dumps() semplicemente non escapava i dizionari controllati dall'utente contenenti chiavi 'lc'. Una mancanza di escaping nel percorso di serializzazione, non di deserializzazione.

È molto più facile notare qualcosa di sbagliato che notare l'assenza di qualcosa, specialmente quando fai audit su load() invece che su dumps(). In uno dei framework AI più attentamente controllati. Due anni e mezzo.
Da lì la ricerca è diventata un esercizio strutturato:
Identificare dove il contenuto non attendibile (principalmente dizionari arbitrari) entra nella serializzazione (output LLM, injection di prompt, input utente, strumenti esterni, documenti estratti).
Identificare quando questi dati serializzati vengono deserializzati.
Identificare cosa un attaccante può ottenere dall'istanziamento arbitrario di oggetti.
A quel punto, il ritrovamento principale era abbastanza chiaro e actionable per un report responsabile: c'era una lacuna nell'escaping in dumps() / dumpd() attorno ai dizionari con chiave 'lc'.
L'avviso successivamente ha catturato ciò che vediamo spesso in pratica: campi come additional_kwargs e response_metadata possono essere influenzati dall'output LLM e dall'injection di prompt, e questi campi possono essere serializzati-deserializzati in molti flussi.
A merito del team LangChain: la risposta e le azioni successive sono state decise, non solo patchare il bug, ma anche irrigidire i default che erano troppo permissivi per il mondo in cui viviamo ora.
Il progetto LangChain ha deciso di assegnare una ricompensa di $4,000 USD per questa scoperta. Secondo huntr, la piattaforma in cui LangChain gestiva il suo programma di bug bounty, sarebbe stato l'importo massimo mai assegnato nel progetto, con ricompense fino ad ora fino a $125.
LangChain serializza determinati oggetti utilizzando un formato di dizionario strutturato. La chiave 'lc' è usata internamente per indicare "questa è una struttura serializzata LangChain", non solo dati utente arbitrari.
È un pattern comune, ma crea un invariante di sicurezza: qualsiasi dato utente che può contenere 'lc' deve essere gestito con cautela. Altrimenti un attaccante può creare un dizionario che "sembra" un oggetto interno e ingannare il deserializzatore per dargli valore.
La patch rende esplicita l'intenzione nella documentazione aggiornata: durante la serializzazione, i dizionari semplici contenenti la chiave 'lc' vengono escapati tramite wrapping.
Ciò impedisce che questi dizionari vengano confusi con i reali oggetti serializzati LangChain durante la deserializzazione.
Le funzioni load()/loads() di LangChain non istanziano classi arbitrarie – verificano contro una whitelist che controlla quali classi possono essere deserializzate. Di default, questa whitelist include classi da langchain_core, langchain_openai, langchain_aws e altri pacchetti dell'ecosistema.
Ecco il trucco: la maggior parte delle classi nella whitelist hanno costruttori innocui. Trovare percorsi sfruttabili ha richiesto di scavare nell'ecosistema alla ricerca di classi che fanno qualcosa di significativo durante l'istanziazione. Quelle che ho trovato sono descritte in dettaglio sotto, ma ce ne potrebbero essere altre in attesa di essere scoperte.
La funzione loads() di LangChain supporta un tipo secret che risolve i valori dalle variabili d'ambiente durante la deserializzazione. Prima della patch, questa funzione secrets_from_env era abilitata per default:
if (
value.get("lc") == 1
and value.get("type") == "secret"
and value.get("id") is not None
):
[key] = value["id"]
if key in self.secrets_map:
return self.secrets_map[key]
if self.secrets_from_env and key in os.environ and os.environ[key]:
return os.environ[key] # <-- Возвращение переменной окружения
return None
Se l'oggetto deserializzato viene restituito all'attaccante, ad esempio la cronologia dei messaggi nel contesto LLM, ciò potrebbe far trapelare variabili d'ambiente.
Ma un percorso più interessante è l'iniezione indiretta di prompt. Anche un attaccante che non può vedere alcuna risposta LLM può esfiltrare segreti istanziando la classe giusta. ChatBedrockConverse di langchain_aws è sia nella whitelist di default di loads sia effettua una richiesta GET durante la costruzione. L'endpoint GET è controllato dall'attaccante e un'intestazione HTTP specifica può essere riempita con una variabile d'ambiente tramite la funzione secrets_from_env.

Questo validatore viene attivato quando ChatBedrockConverse viene istanziata. L'attaccante controlla endpoint_url, innescando una richiesta in uscita. In combinazione con secrets_from_env, l'intestazione aws_access_key_id può essere riempita con qualsiasi variabile d'ambiente – non solo chiavi AWS.
Non pubblichiamo intenzionalmente un exploit completo qui per dare tempo ai team di sicurezza. Tra qualche mese, il sito Huntr li pubblicherà automaticamente.
Tra le classi nella whitelist di default di loads() c'è PromptTemplate. Questa classe crea un prompt da un template, e uno dei formati di template disponibili è Jinja2.
Quando il template viene renderizzato con Jinja2, può essere eseguito codice Python arbitrario. Non abbiamo trovato un modo per innescarlo direttamente dalla sola funzione loads(), ma se una successiva chiamata all'oggetto deserializzato attiva il rendering, ne segue l'esecuzione del codice.
Sospettiamo che possano esserci percorsi per l'esecuzione diretta del codice da loads(), ma non ne abbiamo ancora confermato nessuno. Se hai un'idea solida o un indizio degno di essere testato, saremo lieti di sentirvi – è proprio qui che la comunità della sicurezza aiuta a trasformare le ipotesi in prove. 🤝
Vale anche la pena notare: nelle versioni passate, la classe Chain era anch'essa nella whitelist. Questa classe aveva capacità speciali che potevano consentire un flusso verso il rendering dei template.
La tua applicazione è potenzialmente vulnerabile se utilizza versioni vulnerabili di langchain-core. Ecco alcuni dei pattern vulnerabili più comuni (sono stati identificati 12 flussi in totale):
Tuttavia, il comportamento del sistema è abbastanza complesso che è rischioso presumere che una rapida revisione del codice riveli ogni variante raggiungibile. La cosa più sicura è aggiornare alla versione patchata e non presumere di essere al sicuro finché non lo fai.
Inoltre, l'avviso nota quello che considero il punto più importante del mondo reale:
Il vettore di attacco più comune avviene attraverso campi di risposta LLM come additional_kwargs o response_metadata, che possono essere controllati tramite injection di prompt e poi serializzati/deserializzati in operazioni di streaming.
Questo è esattamente il tipo di incrocio "AI incontra sicurezza classica" in cui le organizzazioni vengono colte alla sprovvista. L'output LLM è un input non attendibile. Se il tuo framework elabora porzioni di quell'output come oggetti strutturati in seguito, devi presumere che gli attaccanti cercheranno di modellarli.
Aggiorna langchain-core alla versione patchata. Se stai usando langchain, langchain-community o altri pacchetti dell'ecosistema, verifica quale versione di langchain-core è effettivamente installata negli ambienti di produzione.
Tratta additional_kwargs, response_metadata, output di strumenti, documenti estratti e cronologia dei messaggi come non attendibili finché non provato il contrario. Questo è particolarmente importante se stai eseguendo lo streaming di log/eventi e poi li reidrati con un caricatore.
Anche dopo l'aggiornamento, attieniti al principio: non abilitare la risoluzione dei segreti dalle variabili d'ambiente a meno che non ti fidi dell'input serializzato. Il progetto ha cambiato i default per un motivo.
Basato sul mio report, c'è un avviso strettamente correlato in LangChainJS (GHSA-r399-636x-v7f6 / CVE-2025-68665) con meccanismi simili: confusione del marcatore 'lc' durante la serializzazione, che consente l'estrazione di segreti e l'istanziazione non sicura in determinate configurazioni.
Se la tua organizzazione esegue sia stack Python che JavaScript di LangChain, consideralo come un promemoria che il pattern si diffonde tra gli ecosistemi: serializzazione tramite marcatore, output di modello non attendibile e successiva deserializzazione sono una forma ricorrente di rischio.
Stiamo entrando in una fase in cui i framework AI agentici stanno diventando infrastruttura critica all'interno dei sistemi di produzione. I formati di serializzazione, i pipeline di orchestrazione, l'esecuzione di strumenti, le cache e il tracing non sono più 'idraulica' – sono parte del vostro perimetro di sicurezza.
Questa vulnerabilità non è "solo un bug in una libreria." È un case study di un pattern più grande:
In Cyata, il nostro lavoro è aiutare le organizzazioni a costruire visibilità, valutazione del rischio, controllo e governance attorno ai sistemi AI – perché se non puoi rispondere rapidamente dove vengono eseguiti gli agenti, quali versioni sono distribuite e quali dati fluiscono attraverso di essi, stai effettivamente volando alla cieca quando arrivano avvisi come questo.
Se sei un leader della sicurezza che sta leggendo questo, ecco la scomoda verità:
La maggior parte delle organizzazioni in questo momento non può rispondere, rapidamente e con sicurezza:
Non è un "problema dello sviluppatore." È un problema di visibilità e governance.
Ed è qui che entra in gioco Cyata.
In Cyata ci concentriamo su risultati pratici: ridurre il rischio AI e agentico senza rallentare i costruttori. Vulnerabilità come questa sono raramente "solo una patch." Rivelano lacune nel modo in cui i team rilevano dove vengono eseguiti gli agenti, comprendono i confini reali di fiducia e garantiscono default più sicuri in framework in rapida evoluzione.
Sappi cosa è in esecuzione, dove e come è connesso.
Rispondi rapidamente alla prima domanda del CVE: siamo vulnerabili e in quali flussi?
Rileva runtime agentici e integrazioni tra ambienti (IDE, CI, servizi, job di lavoro, agenti ospitati).
Tieni traccia di framework, pacchetti e versioni in uso.
Dai priorità a ciò che conta in base al raggio d'azione reale, non solo "la libreria è presente."
Mantieni una triage più rapida: cosa è esposto a internet, cosa riguarda segreti, cosa viene eseguito con privilegi elevati.
Identifica i percorsi a più alto rischio: contenuto non attendibile che fluisce in contesti privilegiati (servizi con segreti, permessi estesi degli strumenti, accesso di rete in produzione).
Evidenzia dove "campi strutturati" possono attraversare confini di fiducia (metadati, output di strumenti, eventi di streaming, artefatti in cache).
Riduci l'esposizione anche prima che ogni dipendenza sia patchata ovunque.
Promuovi default operativi più sicuri: privilegio minimo, confini di isolamento e controlli di policy che si scalano tra i team.
Implementa gateway attorno a pattern rischiosi (ad esempio: deserializzazione di dati non attendibili, ripristino permissivo di oggetti, flussi non sicuri di streaming-in-cache-in-reidratazione).
Limita o gated capacità sensibili in contesti non attendibili (ad esempio: accesso ai segreti dall'ambiente, esecuzione di strumenti con privilegi elevati o esecuzione di percorsi di codice rischiosi in worker privilegiati).
Rendi "l'uso sicuro degli agenti" ripetibile, auditabile e difficile da deviare.
Quando un avviso natalizio cade, l'obiettivo non è l'eroismo – è una risposta calma e controllata, supportata da un inventario reale e gateway assicurati.
Report inviato tramite Huntr – 4 dicembre 2025
Riconosciuto dai manutentori LangChain – 5 dicembre 2025
Avviso e CVE pubblicati – 24 dicembre 2025