Announcement

👇Official Account👇

Welcome to join the group & private message

Article first/tail QR code

Skip to content

LMCache CVE-2026-105192 深度分析:多进程 ZeroMQ 传输层的未认证 RCE——AI 推理基础设施的冷门攻击面 ​

一、事件概览 ​

2026 年 10 月 7 日,JFrog 安全研究团队披露了 LMCache 中的一个未认证代码执行漏洞,编号 CVE-2026-105192,CVSS 9.8(Critical)。攻击者无需任何凭证,只要能访问暴露的 LMCache 端口,就可以在宿主机上执行任意命令——在常见部署模式下,这个命令以 root 权限运行。

LMCache 是 vLLM 生态中广泛使用的 KV Cache 共享层,负责在大语言模型推理服务之间复用预计算好的注意力缓存,显著降低重复前缀(长系统提示、RAG 文档、多轮对话历史)的推理成本。它的典型位置决定了它一旦失守,后果不只是一个组件被拿下,而是整条推理管线的性能护城河变成攻击者的立足点。

最棘手的是:披露时没有可用修复版本,缓解手段只有网络隔离。

二、架构背景:LMCache 的多进程 ZeroMQ 传输 ​

2.1 为什么用 ZeroMQ ​

LMCache 在一个推理节点上通常跑多个进程:vLLM 推理进程、缓存管理进程、以及跨节点同步进程。它们之间需要低延迟、高吞吐的消息传递。ZeroMQ(ZMQ)因其"无 Broker、低延迟、API 极简"的特性成为自然选择。

典型拓扑:

┌────────────────────────────────────────────────┐
│  GPU 节点 A                                     │
│  ┌─────────┐   ZMQ (ipc/tcp)   ┌────────────┐  │
│  │ vLLM #1 │ ◄──────────────► │ LMCache    │  │
│  └─────────┘                  │ Controller │  │
│  ┌─────────┐                  │  (多进程)   │  │
│  │ vLLM #2 │ ◄──────────────► │            │  │
│  └─────────┘                  └─────┬──────┘  │
└──────────────────────────────────────┼─────────┘
                       ZMQ over TCP    │
                       (跨节点同步)     ▼
                                ┌────────────┐
                                │ GPU 节点 B  │
                                │ LMCache    │
                                └────────────┘

问题就出在 tcp:// 跨节点通道上。

2.2 漏洞根因:传输层把"消息"当"指令" ​

根据 JFrog 的分析,问题出在 LMCache 的 multiprocess ZeroMQ transport 实现:该传输层接收的 ZMQ 消息会被处理逻辑中某条路径直接交给本地执行(反序列化 + 派发,且派发目标包含可执行任意 Python 表达式的入口,等效 eval/exec 语义),而这条通道没有认证、没有消息来源校验、没有能力白名单。

用一句话概括:任何一个能连上该 ZMQ 端口的人,都在事实上获得了"在 LMCache 进程里跑代码"的权限。LMCache 在常见 Kubernetes/GPU 主机部署中以 root 运行(为了访问 GPU 设备和宿主挂载),所以 RCE 等于 root shell。

用代码示意漏洞模式(非真实源码,还原根因结构):

python
# 漏洞模式示意:ZMQ server 直接执行不可信消息
import zmq, pickle   # pickle 反序列化本身就是 RCE 原语

context = zmq.Context()
socket = context.socket(zmq.REP)
socket.bind("tcp://0.0.0.0:8100")   # 无认证绑定所有网卡

while True:
    payload = socket.recv()
    obj = pickle.loads(payload)      # ← 攻击者发送恶意 pickle 即获得代码执行
    socket.send(b"ok")

即使实现不用 pickle,只要消息格式中存在被 eval、yaml.load(非 safe loader)、或动态 import 的字段,效果相同。传输层代码最常见的盲区是:作者默认"只有自己的进程会连上来",而忘记了 tcp:// 绑定的是物理网络。

三、攻击链复盘 ​

1. 侦察:Shodan / Censys 上搜索暴露的 LMCache / vLLM 特征端口
        │
2. 连接:攻击者直接 ZMQ connect 目标节点的 tcp:// 端口
        │
3. 注入:构造符合消息格式的 payload,携带可执行表达式
        │
4. 执行:LMCache 进程内 RCE(通常 root)
        │
5. 横向:读取 GPU 节点上的模型权重、API Key、KV 数据;
         以该节点为跳板扫描内网其他推理节点 / 数据库

3.1 为什么说这是 AI 基础设施的系统性问题 ​

这不是 LMCache 一家的问题。2026 年已披露的同类案例已经排成一条线:

漏洞组件根因
CVE-2026-105192LMCacheZMQ 传输未认证执行
CVE-2026-51869DB-GPT v0.8.0沙箱 API 静默回退 LocalRuntime,代码直接跑在宿主机
CVE-2026-9198Langflow未认证 RCE
CVE-2026-72898Metabase未认证 SQL 注入

共同模式:AI 组件的"性能通道"(内部 RPC、管理端口、沙箱旁路)在设计时默认网络是可信的。这些通道往往不在安全审计清单上——团队会加固 HTTP API,却忘了 ZMQ、gRPC、Redis 协议通道。

四、检测与加固 ​

4.1 立即可做的排查 ​

bash
# 1. 找出所有对外监听的 ZMQ 端口
ss -tlnp | grep -v 127.0.0.1

# 2. 确认 LMCache 进程绑定的地址与权限
ps aux | grep -i lmcache
ls -l /proc/$(pgrep -f lmcache | head -1)/fd | grep socket

# 3. 云上环境:检查安全组是否放开了推理节点的高位端口
#    (AWS CLI 示例)
aws ec2 describe-security-groups \
  --filters Name=group-name,Values=*gpu* \
  --query 'SecurityGroups[].{id:GroupId,rules:IpPermissions[?ToPort!=null].[IpProtocol,FromPort,ToPort]}'

4.2 防御清单(按优先级) ​

  1. 网络隔离是当前唯一可靠的缓解(披露时无修复版本)。LMCache 的跨节点 ZMQ 通道必须在私有网络内:
    • K8s 部署:用 NetworkPolicy 限制只有同 namespace 的推理 Pod 可访问
    yaml
    apiVersion: networking.k8s.io/v1
    kind: NetworkPolicy
    metadata:
      name: lmcache-allow-vllm-only
      namespace: llm-serving
    spec:
      podSelector:
        matchLabels:
          app: lmcache
      policyTypes: ["Ingress"]
      ingress:
        - from:
            - podSelector:
                matchLabels:
                  app: vllm
          ports:
            - port: 8100
              protocol: TCP
  2. 禁止 0.0.0.0 绑定。单机场景一律用 ipc://(Unix domain socket)替代 tcp://,文件系统权限天然提供一层控制。
  3. 升级关注:盯住 LMCache 官方 release,修复版本发布后第一时间升级;在此之前把该组件视为高危暴露面。
  4. 给所有 AI 组件做一次"非 HTTP 端口审计"。清单:ZMQ、gRPC、Ray(head 节点默认 8265 dashboard)、Redis、etcd、Jupyter。任何绑在 0.0.0.0 又无认证的都要处理。
  5. 降低进程权限。GPU 访问不需要 root——通过设备插件(K8s device plugin)或 --device 传给非特权用户运行,把 RCE 的破坏上限从 root 压到受限用户。
  6. 监控 ZMQ 端口的外部连接。LMCache 正常运行时连接方是固定几台推理节点,出现新 IP 连接即为告警信号。

4.3 开发者层面:传输层代码的正确写法 ​

如果自己维护类似的内部 RPC,记三条铁律:

  • 默认不信网络:tcp:// 绑定必须显式授权来源(CurveZMQ / mTLS / 至少 token 握手)
  • 禁用可执行反序列化:消息格式用 JSON/MessagePack + schema 校验,永远不要 pickle.loads 网络数据
  • 最小能力派发:消息处理器白名单化,不接受任何形式的动态代码
python
# 正确示范:带 token 握手 + 白名单派发的 ZMQ 服务
import zmq, json, hmac

SECRET = os.environb[b"LMCACHE_CHANNEL_SECRET"]
ALLOWED = {"cache_get", "cache_put", "stats"}

ctx = zmq.Context()
sock = ctx.socket(zmq.REP)
sock.bind("tcp://10.0.1.5:8100")   # 只绑内网 IP,不绑 0.0.0.0

while True:
    raw = sock.recv()
    try:
        msg = json.loads(raw)               # JSON,杜绝 pickle
        mac = hmac.new(SECRET, msg["body"].encode(),
                       "sha256").hexdigest()
        assert hmac.compare_digest(mac, msg["mac"])   # 认证
        body = json.loads(msg["body"])
        assert body["op"] in ALLOWED                  # 白名单
        result = handlers[body["op"]](body.get("args", {}))
        sock.send(json.dumps({"ok": True, "data": result}).encode())
    except Exception as e:
        sock.send(json.dumps({"ok": False, "err": "rejected"}).encode())

五、结语 ​

CVE-2026-105192 的教训不是"LMCache 写了烂代码",而是整个 AI 基础设施层的默认网络信任模型该更新了。GPU 节点是 2026 年最值钱的服务器之一——上面有模型权重、有客户数据、有计费凭证。性能优化组件(KV Cache、分布式推理、向量检索)大量使用高性能无认证协议,这在天真的局域网假设下成立,在真实生产网络中就是一颗颗等着被点名的雷。

检查你的推理集群,先从 ss -tlnp 开始。

参考资料 ​

上次更新于: