Breach report
LangGrinch: a serialization escaping bug turns LLM output into secret theft and RCE in LangChain Core
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.81or later. - Never deserialize model output or user data with secret-loading enabled; keep
secrets_from_envoff 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.