Gradio has SSRF via Malicious `proxy_url` Injection in `gr.load()` Config Processing
Description
Gradio is an open-source Python package designed for quick prototyping. Prior to version 6.6.0, a Server-Side Request Forgery (SSRF) vulnerability in Gradio allows an attacker to make arbitrary HTTP requests from a victim's server by hosting a malicious Gradio Space. When a victim application uses gr.load() to load an attacker-controlled Space, the malicious proxy_url from the config is trusted and added to the allowlist, enabling the attacker to access internal services, cloud metadata endpoints, and private networks through the victim's infrastructure. Version 6.6.0 fixes the issue.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
Gradio before 6.6.0 has an SSRF vulnerability where a malicious Space's proxy_url is trusted, allowing attackers to make arbitrary HTTP requests from the victim's server.
Vulnerability
Overview
Gradio is an open-source Python package for building machine learning demos. Prior to version 6.6.0, a Server-Side Request Forgery (SSRF) vulnerability exists in the gr.load() function's config processing. When a victim application loads an attacker-controlled Space, the malicious proxy_url from the Space's configuration is trusted and added to the allowlist without proper validation [1][2]. This allows the attacker to specify arbitrary internal or cloud metadata endpoints as the proxy target.
Exploitation
Mechanism
The attack chain begins when a victim uses gr.load() to load a Space hosted by an attacker. The attacker crafts a Space configuration containing a proxy_url pointing to an internal service (e.g., http://169.254.169.254/ for AWS metadata) or any private network resource [4]. Gradio's built-in /proxy={url_path}` endpoint then proxies requests to that URL, and the host-based validation only checks if the URL's host matches any trusted proxy_url host, which now includes the attacker-controlled value [4]. No authentication is required beyond the victim loading the malicious Space.
Impact
An attacker can leverage this SSRF to access internal services, cloud metadata endpoints, and private networks through the victim's infrastructure. This includes cloud metadata services (e.g., AWS, GCP), internal APIs, and other resources that would otherwise be inaccessible from the internet. The victim's server becomes a proxy for the attacker's requests, potentially leading to credential theft, data exfiltration, or lateral movement within the victim's network [2][4].
Mitigation
The vulnerability is fixed in Gradio version 6.6.0. The fix validates that the proxy_url host ends with .hf.space before adding it to the allowlist, preventing SSRF via malicious configs [3]. Users should upgrade to version 6.6.0 or later. No workaround is available for earlier versions.
AI Insight generated on May 18, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
gradioPyPI | < 6.6.0 | 6.6.0 |
Affected products
2- Range: <6.6.0
- gradio-app/gradiov5Range: < 6.6.0
Patches
1fc7c01ea1e58Validate proxy url host (#12882)
6 files changed · +50 −9
.changeset/kind-planes-tickle.md+6 −0 added@@ -0,0 +1,6 @@ +--- +"gradio": patch +"gradio_client": patch +--- + +fix:Validate proxy url host
client/python/gradio_client/documentation.py+4 −1 modified@@ -215,7 +215,10 @@ def document_fn(fn: Callable, cls) -> tuple[str, list[dict], dict, str | None]: return_docs = {} else: return_doc_text = "\n".join(returns) - return_docs = {"annotation": signature.return_annotation, "doc": return_doc_text} + return_docs = { + "annotation": signature.return_annotation, + "doc": return_doc_text, + } examples_doc = "\n".join(examples) if len(examples) > 0 else None return description_doc, parameter_docs, return_docs, examples_doc
gradio/blocks.py+7 −2 modified@@ -1206,7 +1206,9 @@ def from_config( config = copy.deepcopy(config) components_config = config["components"] original_mapping: dict[int, Block] = {} - proxy_urls = {proxy_url} + proxy_urls: set[str] = set() + if httpx.URL(proxy_url).host.endswith(".hf.space"): + proxy_urls.add(proxy_url) def get_block_instance(id: int) -> Block: for block_config in components_config: @@ -1230,7 +1232,10 @@ def get_block_instance(id: int) -> Block: block_proxy_url = block_config["props"]["proxy_url"] block.proxy_url = block_proxy_url - proxy_urls.add(block_proxy_url) + # Only add proxy URLs that point to known Hugging Face Space + # hosts to prevent SSRF via malicious configs. + if httpx.URL(block_proxy_url).host.endswith(".hf.space"): + proxy_urls.add(block_proxy_url) if ( _selectable := block_config["props"].pop("_selectable", None) ) is not None:
gradio/components/annotated_image.py+2 −2 modified@@ -132,7 +132,7 @@ def preprocess( Parameters: payload: Dict of base image and list of annotations. Returns: - Passes its value as a `tuple` consisting of: + Passes its value as a `tuple` consisting of: - `str` filepath to a base image - `list` of annotations. -- Each annotation itself is a `tuple` of a mask (as a `str` filepath to image) and a `str` label. @@ -155,7 +155,7 @@ def postprocess( ) -> AnnotatedImageData | None: """ Parameters: - value: Expects a tuple consisting of a base image and list of annotations: a `tuple[Image, list[Annotation]]`. + value: Expects a tuple consisting of a base image and list of annotations: a `tuple[Image, list[Annotation]]`. - The `Image` itself can be `str` filepath, `numpy.ndarray`, or `PIL.Image`. - Each `Annotation` is a `tuple[Mask, str]`. -- The `Mask` can be either a `tuple` of 4 `int`'s representing the bounding box coordinates (x1, y1, x2, y2), or 0-1 confidence mask in the form of a `numpy.ndarray` of the same shape as the image.
gradio/routes.py+5 −2 modified@@ -367,9 +367,12 @@ def build_proxy_request(self, url_path): ) if not is_safe_url: raise PermissionError("This URL cannot be proxied.") - is_hf_url = url.host.endswith(".hf.space") + # Only allow proxying to Hugging Face Space URLs to prevent SSRF + # via malicious proxy_url values in untrusted configs. + if not url.host.endswith(".hf.space"): + raise PermissionError("This URL cannot be proxied.") headers = {} - if Context.token is not None and is_hf_url: + if Context.token is not None: headers["Authorization"] = f"Bearer {Context.token}" rp_req = client.build_request("GET", url, headers=headers) return rp_req
test/test_routes.py+26 −2 modified@@ -633,8 +633,32 @@ def test_proxy_does_not_leak_hf_token_externally(self): "https://gradio-tests-test-loading-examples-private.hf.space/file=Bunny.obj" ) assert "authorization" in dict(r.headers) - r = app.build_proxy_request("https://google.com") - assert "authorization" not in dict(r.headers) + with pytest.raises(PermissionError): + app.build_proxy_request("https://google.com") + + def test_proxy_rejects_non_hf_space_urls(self): + """Proxy should reject non-.hf.space URLs even if they are in proxy_urls, + to prevent SSRF via malicious proxy_url injection in configs.""" + app = routes.App() + interface = gr.Interface(lambda x: x, "text", "text") + interface.proxy_urls = { + "https://gradio-tests-test-loading-examples-private.hf.space", + "http://169.254.169.254", + "http://internal-service.local", + } + app.configure_app(interface) + # .hf.space URL should work + app.build_proxy_request( + "https://gradio-tests-test-loading-examples-private.hf.space/file=Bunny.obj" + ) + # AWS metadata endpoint should be blocked + with pytest.raises(PermissionError): + app.build_proxy_request( + "http://169.254.169.254/latest/meta-data/iam/security-credentials/" + ) + # Internal service should be blocked + with pytest.raises(PermissionError): + app.build_proxy_request("http://internal-service.local/admin") def test_can_get_config_that_includes_non_pickle_able_objects(self): my_dict = {"a": 1, "b": 2, "c": 3}
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
5- github.com/advisories/GHSA-jmh7-g254-2cq9ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2026-28416ghsaADVISORY
- github.com/gradio-app/gradio/commit/fc7c01ea1e581ef70be98fddf003b0c91315c7ccghsaWEB
- github.com/gradio-app/gradio/releases/tag/gradio%406.6.0ghsaWEB
- github.com/gradio-app/gradio/security/advisories/GHSA-jmh7-g254-2cq9ghsax_refsource_CONFIRMWEB
News mentions
0No linked articles in our index yet.