lm-sys / lm-sys/FastChat

Unauthenticated SSRF and worker/model spoofing via the controller /register_worker endpoint

Open
#3,886 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Python
Stars
39.5k
Forks
4.8k
PR merge metrics
No merged PRs in 30d

Description

### 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`:

```python
@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.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in fastchat/serve/controller.py at register_worker, get_worker_status, receive_heart_beat, and the worker proxy paths; compare the authentication approach in openai_api_server, where check_api_key is mentioned. Done means unauthenticated worker-management requests cannot trigger fetches or registrations, and worker_name is restricted to expected addresses without exposing the controller to untrusted networks.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
api, backend, security
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
38/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.