Skip to content

[BUG]通过 zmq.Socket.recv_pyobj 进行的隐式 Pickle 反序列化导致远程代码执行 (RCE) #784

Open
@kexinoh

Description

@kexinoh

HttpServerManager 类中,当服务配置为多节点模式 (args.nnodes > 1) 且节点 rank 大于 0 时,代码使用 zmq.PULL socket 绑定到 tcp://*:{args.multinode_httpmanager_port}。 这意味着该端口监听来自所有网络接口的连接,潜在地将其暴露给不受信任的网络。
lightllm/server/httpserver/manager.py

# 在 HttpServerManager.__init__ 中:
        if args.nnodes > 1:
            if args.node_rank == 0:
                # ... (rank 0 code)
            else:
                context = zmq.asyncio.Context(2)
                self.multinode_req_manager = context.socket(zmq.PULL)
                # VULNERABLE BINDING: Binds to all interfaces, potentially exposing to wider network
                self.multinode_req_manager.bind(f"tcp://*:{args.multinode_httpmanager_port}")
                logger.info(
                    f"HttpServerManager listening for child node requests on *:{args.multinode_httpmanager_port}"
                )

随后,在 loop_for_request 协程中,代码调用 self.multinode_req_manager.recv_pyobj() 来接收来自主节点(rank 0)或潜在的任何网络来源的数据。

# 在 HttpServerManager.loop_for_request 中:
        async def loop_for_request(self):
            assert self.args.node_rank > 0
            tasks = []
            self.request_order_queue = []
            while True:
                (
                    prompt,
                    sampling_params,
                    multimodal_params,
                    # VULNERABLE CALL: Implicitly uses pickle.loads() to deserialize received bytes
                ) = await self.multinode_req_manager.recv_pyobj()
                # ... (process received objects)

zmq.Socket.recv_pyobj() 方法在其内部实现中**隐式地使用 pickle.loads()**将接收到的字节流反序列化回 Python 对象。

安全风险:

pickle 模块对于反序列化来自不受信任来源的数据是不安全的。攻击者可以构造一个恶意的 pickle payload,当这个 payload 被 recv_pyobj() 内部的 pickle.loads() 处理时,会导致在服务器上执行任意代码。

结合 tcp://* 绑定,这使得任何能够访问运行非 rank-0 节点的服务器上指定端口 (args.multinode_httpmanager_port) 的攻击者,都有可能发送恶意 pickle 数据并获得远程代码执行能力。

复现步骤 (概念性):

  1. 以多节点模式启动服务 (args.nnodes > 1),确保至少有一个 rank 大于 0 的 HttpServerManager 实例运行。
  2. 确定 rank > 0 的节点正在监听的 IP 地址和端口 (args.multinode_httpmanager_port)。由于绑定的是 *,这通常是服务器的所有 IP 地址。
  3. 从一个可访问该 IP 和端口的攻击者机器,使用 ZMQ 连接到目标 socket。
  4. 攻击者使用 pickle.dumps() 创建一个包含恶意代码(例如,反弹 shell 或执行系统命令的类/函数)的 Python 对象,并将其序列化为字节流。
  5. 攻击者通过 ZMQ 连接将这个恶意的字节流发送给目标服务器。
  6. 服务器上的 recv_pyobj() 调用接收到恶意字节流,并(隐式地)使用 pickle.loads() 进行反序列化,从而触发恶意代码的执行。

影响:

成功利用此漏洞允许攻击者在运行易受攻击的 HttpServerManager 节点的服务器上以运行该服务进程的用户权限执行任意代码。这可能导致服务器被完全控制、数据泄露、服务中断等严重后果。

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions