Breachwire.riscent

Breach report

LangGrinch: a serialization escaping bug turns LLM output into secret theft and RCE in LangChain Core

AI & LLM BreachesCritical (CVSS 9.3)CVE-2025-686642025-12
The bottom lineLangChain Core's serializer failed to escape user dictionaries carrying LangChain's internal 'lc' marker, so attacker-controlled data — reachable through prompt injection — was rehydrated as real LangChain objects, enabling environment-secret extraction and remote code execution.
Category
AI & LLM Breaches
Type
CVE · LangChain Core (langchain-core)
Date
2025-12
Severity
Critical (CVSS 9.3)
OWASP
LLM03 Supply Chain; LLM05 Improper Output Handling
CWE / CVE
CWE-502 CVE-2025-68664

What happened

LangChain Core is the foundational library beneath the LangChain framework used by a huge number of LLM applications. In December 2025 a critical flaw nicknamed LangGrinch, tracked as CVE-2025-68664 (CVSS 9.3), was disclosed by Yarden Porat of Cyata and reported on December 4, 2025.

The library's serialization functions, dumps() and dumpd(), did not escape free-form dictionaries that contained an lc key. That key is LangChain's own internal marker identifying a serialized LangChain object. Because user-controlled data carrying an lc structure was passed through unescaped, when it was later deserialized the library treated ordinary attacker data as a legitimate LangChain object. The most practical route in: LLM response fields such as additional_kwargs or response_metadata, which an attacker can steer through prompt injection. From there researchers showed three impacts — automatic extraction of environment variables when deserialization ran with secrets_from_env enabled, instantiation of unsafe objects inside the trusted langchain* namespaces, and remote code execution via Jinja2 templates. Fixed versions are langchain-core 1.2.5 (for the 1.x line) and 0.3.81 (for 0.3.x); the JavaScript packages carry the parallel CVE-2025-68665.

Root cause

Untrusted data crossed a trust boundary on deserialization. The serializer assumed any dictionary bearing the lc marker was self-produced and safe, but never escaped user dictionaries that happened to carry that marker. Rehydrating attacker-shaped data as trusted objects is classic unsafe deserialization (CWE-502) — OWASP LLM05 Improper Output Handling, made reachable because LangChain Core is a widely embedded dependency (LLM03 Supply Chain).

How it would have been caught

A round-trip fuzz test — serialize then deserialize a dictionary containing a hostile lc payload and assert no object outside an explicit allowlist is instantiated and no environment variable is read — would have surfaced it. Software Composition Analysis flags the vulnerable langchain-core range in a dependency tree immediately. The reproduction is a single dumps()loads() cycle over an LLM message whose additional_kwargs carries the crafted marker.

How to prevent it

  • Upgrade langchain-core to 1.2.5 / 0.3.81 or later.
  • Never deserialize model output or user data with secret-loading enabled; keep secrets_from_env off on any untrusted path.
  • Escape or reject reserved markers in user-supplied dictionaries, and constrain deserialization to a strict class allowlist rather than a trusted namespace prefix.
  • Treat LLM response fields as untrusted input, not as structured trusted objects.

The Breachwire test (red → green)

Feed an LLM message whose additional_kwargs contains the crafted lc payload through dumps()/loads() on the vulnerable version and confirm a secret is read or an unexpected object is instantiated (the RED control proving the vector). Upgrade and disable env-secret loading, then confirm the same payload deserializes to inert data while a legitimate LangChain object still round-trips correctly.