Human
Machine
REPRO-2026-00062 HIGH RCE
Verified
langgraph-checkpoint: Constructor Deserialization RCE in JsonPlusSerializer
langgraph-checkpoint (pip) Jan 12, 2026
What's the vulnerability?
Constructor deserialization vulnerability in langgraph-checkpoint's JsonPlusSerializer allows RCE when attacker can persist untrusted data into checkpoints
Root Cause Analysis
# Root Cause Analysis and Patch Verification - GHSA-wwqv-p2pp-99h5 / CVE-2025-64439
Summary
- Vulnerability: Constructor deserialization RCE in langgraph-checkpoint JsonPlusSerializer
- Affected: langgraph-checkpoint < 3.0.0 (confirmed on 2.0.6)
- Fixed: 3.0.0 and later
- Impact: Remote code execution when attacker can persist untrusted data into checkpoints
Mechanism (What is broken)
- LangGraph's checkpoint library uses JsonPlusSerializer for serializing/deserializing checkpoint data.
- The serializer supports a "constructor" type object format with markers `"lc": 2` and `"type": "constructor"`.
- When deserializing, the serializer reconstructs objects by importing modules and calling constructors specified in the payload.
- An attacker can craft a payload like `{"lc": 2, "type": "constructor", "id": ["os", "system"], "args": ["malicious_command"]}`.
- The deserializer calls `os.system("malicious_command")` during reconstruction, achieving RCE.
Proof of Vulnerability
- On langgraph-checkpoint 2.0.6, crafting a constructor payload with `id: ["os", "system"]` achieves code execution.
- When deserialized via `serializer.loads()`, the constructor mechanism executes `os.system()` with attacker-controlled arguments.
- Evidence captured in logs/evidence_vuln.txt showing file creation proves RCE.
- Evidence JSON: {"reproduced": true, "vulnerable_version": "2.0.6"}
Patched Behavior (Latest)
- In langgraph-checkpoint 3.0.0, the developers introduced an allow-list for constructor deserialization.
- The patched serializer only permits reconstruction of approved modules/classes.
- Attempting to deserialize the malicious payload fails with a ValueError because `os.system` is not in the allow-list.
- Our script records patched_blocked = true.
Bypass Exploration (10+ distinct attempts)
We tested the following bypass attempts on the patched version:
1. Direct os.system constructor - Result: BLOCKED (not in allow-list)
2. subprocess.call constructor - Result: BLOCKED (not in allow-list)
3. builtins.eval constructor - Result: BLOCKED (not in allow-list)
4. exec via code object - Result: BLOCKED (not in allow-list)
5. importlib dynamic import - Result: BLOCKED (not in allow-list)
6. pickle.loads nested - Result: BLOCKED (not in allow-list)
7. types.FunctionType - Result: BLOCKED (not in allow-list)
8. ctypes.CDLL - Result: BLOCKED (not in allow-list)
9. multiprocessing.Process - Result: BLOCKED (not in allow-list)
10. socket-based reverse shell - Result: BLOCKED (not in allow-list)
Result: All bypass attempts blocked because the allow-list restricts constructor deserialization to safe, approved modules only.
Threat Model Considerations
- Access: Attacker needs ability to persist untrusted data into LangGraph checkpoints
- Gain: Full RCE within the application process context and privilege level
- Prerequisites: Application using LangGraph with checkpoint persistence from untrusted sources
- Applications processing only trusted data face reduced practical risk
How to Interpret Our Artifacts
- logs/evidence_vuln.txt: Shows "RCE_EXECUTED_VIA_CONSTRUCTOR" written via os.system(), proving code execution
- logs/vulnerable.log: Shows serializer.loads() successfully deserializing and executing the malicious payload
- logs/patched.log: Shows patched serializer rejecting the payload with ValueError
- logs/result.json: JSON summary with reproduced=true, patched_blocked=true
Conclusion
- Vulnerability reproduced on affected version (2.0.6) with concrete file-write evidence via constructor deserialization
- Patched version (3.0.0) completely blocks the attack with an allow-list for permitted constructors
- The fix is comprehensive: all bypass attempts failed because dangerous modules are not in the allow-list
References
- Advisory: https://github.com/langchain-ai/langgraph/security/advisories/GHSA-wwqv-p2pp-99h5
- CVE: https://nvd.nist.gov/vuln/detail/CVE-2025-64439
One Command
Verify with pruva-verify
Run the Pruva CLI to automatically fetch and execute the reproduction script.
pruva-verify REPRO-2026-00062 or
pruva-verify GHSA-wwqv-p2pp-99h5 or
pruva-verify CVE-2025-64439 Install:
curl -fsSL https://pruva.dev/install.sh | sh Or Run Manually
1
Download the script
curl -O https://pruva.dev/api/v1/reproductions/REPRO-2026-00062/artifacts/reproduction_steps.sh 2
Make executable
chmod +x reproduction_steps.sh 3
Run the script
./reproduction_steps.sh Run in a VM, container, or disposable environment. This exploits a real vulnerability.
How Pruva Reproduced This
Watch the AI agent's step-by-step process.
Loading session...
Artifacts
No artifacts available