trust_remote_code=False 拦不住 LlavaOnevision2 处理器,恶意模型仓库直接执行代码

今天雷达只扫到一起,没有横向对比可做。它也不是 prompt injection 那一类——没有人去说服模型做什么,被利用的是「模型文件本身就是可执行代码」这条加载语义,出问题的是模型服务框架的信任开关没被一致地遵守。

CVE-2026-90553 · vLLM

披露 2026-09-12。CVSS 口径不一致:入库记录给的是 7.8 HIGH,公开聚合站(cvefeed / OffSeq)给的是 CVSS 4.0 8.5 HIGH,攻击向量标为 Local。CWE-94(代码注入)。影响 0.28.0 之前的所有版本,0.28.0 修复。

先说 trust_remote_code 是什么。HuggingFace 上的模型仓库不只有权重,还可以带自己的 Python 文件——modeling_xxx.pyprocessing_xxx.py。加载这种模型时,框架要 import 并运行这些文件,才能知道怎么把图片切成 patch、怎么拼 chat template。所以 transformers 和 vLLM 都给了一个开关:trust_remote_code=True 才允许执行仓库里的代码,默认 False 应当直接拒绝加载。运维把它设成 False,意思就是「这个仓库我不认识,只准读权重,不准跑它的脚本」。

攻击链:攻击者在模型 Hub(或任何 vLLM 能拉到的仓库路径)上放一个看起来正常的多模态模型,config 里声明用 LlavaOnevision2 这一路处理器,仓库里附带一个 processing_llava_onevision2.py,文件顶层塞进任意代码。受害方是个自觉的运维:起服务时明确写了 --trust-remote-code=False,以为自己只会加载权重。vLLM 走到 LlavaOnevision2 的 processor 加载分支时没有把这个 flag 传下去,照样把远程 processor 类 import 进来——文件顶层的代码就在这一刻执行,权限等于 vLLM 进程本身。推理服务器上通常有什么?HF token、S3/对象存储凭证、内网可达的其他推理节点、整块 GPU。全都拿到了。具体 PoC 载荷、patch commit 号、是否有在野利用,都没查到,不编。

模型在这条链里是投递载体,不是被骗的一方。整条链里模型没有生成任何东西,甚至不需要跑起来一次前向——危险发生在「读模型」这一步。这就是为什么它算 LLM 基础设施事故而不是普通的 Python 反序列化问题:只有在「一个模型 = 权重 + 预处理代码 + 自定义 modeling 代码」这种约定下,下载一个模型才等价于下载并执行一段第三方程序,而 trust_remote_code 是这套约定里唯一的闸门。

厂商修的是哪一层:0.28.0 修的是这一条分支的参数传递,让 LlavaOnevision2 处理器加载路径也遵守 trust_remote_code。入口侧没修,大概率也修不了——「从公网 Hub 拉一个来路不明的模型仓库」这个动作本身仍然是默认允许的,没有强制签名校验,没有把模型代码丢进沙箱或独立进程去跑。也就是说,修好的是一个开关的一根线,而不是「执行陌生代码」这件事。

同样的形状在 vLLM 里出现过不止一次:CVE-2026-27893 是 nemotron_vl.pykimi_k25.py 在加载子组件时把 trust_remote_code=True 硬编码进去,同样绕过用户显式的 opt-out,影响 0.10.1 到 0.17.x,0.18.0 修复。一个是硬编码 True,一个是忘了往下传——两次都是同一个开关在某条支线上失效。

已核实来源


本文由自动化管道生成(采集 → 逐字核验 → 模型撰写),未经人工改写。