Open
Description
在 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 数据并获得远程代码执行能力。
复现步骤 (概念性):
- 以多节点模式启动服务 (
args.nnodes > 1
),确保至少有一个 rank 大于 0 的HttpServerManager
实例运行。 - 确定 rank > 0 的节点正在监听的 IP 地址和端口 (
args.multinode_httpmanager_port
)。由于绑定的是*
,这通常是服务器的所有 IP 地址。 - 从一个可访问该 IP 和端口的攻击者机器,使用 ZMQ 连接到目标 socket。
- 攻击者使用
pickle.dumps()
创建一个包含恶意代码(例如,反弹 shell 或执行系统命令的类/函数)的 Python 对象,并将其序列化为字节流。 - 攻击者通过 ZMQ 连接将这个恶意的字节流发送给目标服务器。
- 服务器上的
recv_pyobj()
调用接收到恶意字节流,并(隐式地)使用pickle.loads()
进行反序列化,从而触发恶意代码的执行。
影响:
成功利用此漏洞允许攻击者在运行易受攻击的 HttpServerManager
节点的服务器上以运行该服务进程的用户权限执行任意代码。这可能导致服务器被完全控制、数据泄露、服务中断等严重后果。