Summary
The FastChat controller exposes a /register_worker endpoint with no authentication. The request body field worker_name is an attacker-controlled address that the controller immediately fetches (requests.post(worker_name + "/worker_get_status")) and then stores as a serving worker. This gives an unauthenticated client that can reach the controller two primitives: server-side request forgery into the cluster's private network, and worker/model spoofing, where the attacker registers their own address under a model name so the controller proxies every user generation request (prompts and images) to the attacker and streams the attacker's response back to clients. Confirmed against the real booted controller: /register_worker fetched an internal listener and registered a spoofed model.
Details
fastchat/serve/controller.py:
@app.post("/register_worker") # ~line 288: no auth dependency
def register_worker(request: Request): ...
# register_worker (~line 75) -> get_worker_status (~line 106):
r = requests.post(worker_name + "/worker_get_status", timeout=5) # SSRF: worker_name from the request body
The controller FastAPI app has no authentication on any endpoint (unlike openai_api_server, which has check_api_key). data["worker_name"] is a fully attacker-controlled URL. Once registered, the controller routes user traffic to it: worker_api_generate_stream (~lines 266 to 280) does requests.post(worker_addr + "/worker_generate_stream", json=params), forwarding the user's full prompt (and images) to the registered address, and /get_worker_address / /list_models hand the attacker address and model name to clients.
The default bind is localhost (~line 355), but FastChat's documented multi-node deployment runs the controller with --host 0.0.0.0 behind the 0.0.0.0-bound OpenAI API server and gradio frontends, so the controller is reachable on the network in real deployments.
PoC
Impact
An unauthenticated client that can reach the controller can (1) make the controller issue requests to arbitrary internal addresses (SSRF / internal port-probing across the worker mesh), and (2) register a malicious worker under a victim model name so the controller proxies every user generation request to the attacker, exfiltrating user prompts/images and poisoning the responses returned to all clients of that model. In the documented network-exposed multi-node deployment this is a high-impact unauthenticated compromise of the serving cluster.
Remediation
Require authentication on the controller's worker-management endpoints (/register_worker, /receive_heart_beat, etc.) with a shared secret known only to legitimate workers, and validate/allowlist worker_name against expected worker addresses (reject internal/loopback/metadata targets and arbitrary hosts). Do not fetch a client-supplied address before authenticating the registrant. Document that the controller must not be exposed to untrusted networks.
Summary
The FastChat controller exposes a
/register_workerendpoint with no authentication. The request body fieldworker_nameis an attacker-controlled address that the controller immediately fetches (requests.post(worker_name + "/worker_get_status")) and then stores as a serving worker. This gives an unauthenticated client that can reach the controller two primitives: server-side request forgery into the cluster's private network, and worker/model spoofing, where the attacker registers their own address under a model name so the controller proxies every user generation request (prompts and images) to the attacker and streams the attacker's response back to clients. Confirmed against the real booted controller:/register_workerfetched an internal listener and registered a spoofed model.Details
fastchat/serve/controller.py:The controller FastAPI app has no authentication on any endpoint (unlike
openai_api_server, which hascheck_api_key).data["worker_name"]is a fully attacker-controlled URL. Once registered, the controller routes user traffic to it:worker_api_generate_stream(~lines 266 to 280) doesrequests.post(worker_addr + "/worker_generate_stream", json=params), forwarding the user's full prompt (and images) to the registered address, and/get_worker_address//list_modelshand the attacker address and model name to clients.The default bind is
localhost(~line 355), but FastChat's documented multi-node deployment runs the controller with--host 0.0.0.0behind the0.0.0.0-bound OpenAI API server and gradio frontends, so the controller is reachable on the network in real deployments.PoC
Impact
An unauthenticated client that can reach the controller can (1) make the controller issue requests to arbitrary internal addresses (SSRF / internal port-probing across the worker mesh), and (2) register a malicious worker under a victim model name so the controller proxies every user generation request to the attacker, exfiltrating user prompts/images and poisoning the responses returned to all clients of that model. In the documented network-exposed multi-node deployment this is a high-impact unauthenticated compromise of the serving cluster.
Remediation
Require authentication on the controller's worker-management endpoints (
/register_worker,/receive_heart_beat, etc.) with a shared secret known only to legitimate workers, and validate/allowlistworker_nameagainst expected worker addresses (reject internal/loopback/metadata targets and arbitrary hosts). Do not fetch a client-supplied address before authenticating the registrant. Document that the controller must not be exposed to untrusted networks.