要是你把 vLLM 的 /v1 接口开在内网里谁都能调、甚至摆到了公网上,这两天得抽空看一眼——它一次性补了十个洞。

10 月 25 日挂出来的一批公告,编号从 CVE-2026-116172 排到 CVE-2026-116181,几乎全是"远程就能触发",性质无非三类:把服务当跳板往外连、拿一张图/一段音把 worker 撑爆、或者干脆让推理引擎崩掉。修法倒是统一:升级。

先挑两个最扎眼的说。

一、一个请求头,就能让它连去别人的机器(SSRF),CVE-2026-116176,评分 8.8

vLLM 的 P2P NCCL 连接器在收预填充(prefill)任务时,会把某些请求里的字段当成"解码地址"用。而 X-Request-Id 这个请求头,恰恰能被调用方塞进这样一个地址。结果就是:攻击者发一个正常请求,worker 的 ZMQ 会主动去连攻击者指定的主机——搭上去之后,泄露出去的是 NCCL 的 unique_id 和 KV-cache 张量。换句话说,推理缓存可能被端走;退一步,也能直接把 worker 卡死。影响 0.9.2 到 0.24.0 之前。

二、一张"解压炸弹"图,把 worker OOM 掉,CVE-2026-116177,评分 8.7

vLLM 的 Nemotron-VL 系列处理器里,把图片的像素上限 Image.MAX_IMAGE_PIXELS 全局设成了 None——也就是把图像库那层防"解压炸弹"的阈值关掉了。于是往 /v1/chat/completions 发一张精心构造的图,就能让进程内存一路涨到被 OOM kill。而且这不是只坑自己:同一台机器上一起跑的其他租户,跟着一起受影响。影响 0.10.2 到 0.25.0 之前。

这一批里剩下的,也都不是善茬:

  • CVE-2026-116179(8.7):通过 media_io_kwargs 覆盖 num_frames,绕过视频/JPEG 的帧数上限,把服务器资源耗干;
  • CVE-2026-116178(8.2):给分离式推理(disaggregated)接口发不一致的多模态元数据,直接把引擎搞崩;
  • CVE-2026-116180(8.3):LogprobsTensors.empty_cpu() 分配了未初始化的 int32 张量,叠加模型运行器里遗留的 prompt 输出,触发 DoS;
  • CVE-2026-116172 / 116173 / 116174(各 8.2):Rust 写的 gRPC 前端——绕过 max_logprobs 上限、把未校验的 prompt token id 直接转给 EngineCore、以及能让 EngineCore 终止,三条凑一块;
  • CVE-2026-116175(7.1):音频多模态路径调 load_audio 时没带 max_duration_s,又一个解压炸弹;
  • CVE-2026-116181(7.1):不合法的越界 stop_token_ids,也能把引擎打崩。

公告都在 vLLM 仓库的 security advisories 里,比如 SSRF 那条 GHSA-vfp2-c8pq-v6h6、解压炸弹那条 GHSA-mm38-5g96-7j6x。

先自查版本,一行:

python -c "import vllm; print(vllm.__version__)"

(容器里就跑 pip show vllm,看你镜像里装的是哪个。)

修复:这批洞是分开在 0.24.0、0.25.0、0.29.0、0.30.0 到 0.31.0 里陆续修的,一个个对号入座太累,直接升到 0.31.0 以上就行,PyPI 上当前最新是 0.32.0。跑在 K8s 里的,记得把镜像 tag 也一起改,别只改了本地的 venv。

还有一句顺手的:vLLM 的 /v1 默认不带认证。就算这次全升到位,把一个没有任何鉴权的模型接口摆在内网里,本身也是一颗雷——至少放到反代和鉴权后面。

vLLM 版本自查:低于 0.31.0 需升级,≥ 0.31.0 已修

写 Pillow 那个"构造图片越界写"的洞 时聊过一句:解析不受信任数据的地方,出事基本都在解码器里。vLLM 这次更前卫一点——出事的不是解码器本身的越界,而是"把阈值关掉"和"把请求头当地址用"这种配置面、协议面的疏忽。升完版本,我大概还得回去数一数自己到底有几个 /v1 是没挂鉴权裸着的,这个才真让人睡不着。