VAITP Dataset

← Back to the dataset

CVE-2024-48052

Gradio SSRF in DownloadButton allows local resource access.

  • CVSS 6.5
  • CWE-918
  • Configuration Issues
  • Remote

In gradio <=4.42.0, the gr.DownloadButton function has a hidden server-side request forgery (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. This can lead to the download of local resources and sensitive information.

CVSS base score
6.5
Published
2024-11-04
OWASP
A10 Server-Side Request Forgery (SSRF)
Orthogonal defect classification
Interface
Code defect classification
Incorrect Functionality
Category
Configuration Issues
Subcategory
Server-Side Request Forgery (SSRF)
Accessibility scope
Remote
Impact
Information Disclosure
Affected component
Gradio
Fixed by upgrading
Yes

Solution

Upgrade to gradio>=4.43.0

Vulnerable code sample

import gradio as gr
import requests
import os

def save_url_to_cache(url, filename):
    """
    Saves the content from a given URL to a local file.
    
    Args:
        url: The URL to fetch content from.
        filename: The name of the file to save the content to.
    """
    try:
        response = requests.get(url, stream=True)
        response.raise_for_status()  # Raise HTTPError for bad responses (4xx or 5xx)

        with open(filename, 'wb') as file:
            for chunk in response.iter_content(chunk_size=8192):
                file.write(chunk)
        return filename  # Return the filename to confirm success
    except requests.exceptions.RequestException as e:
        return f"Error downloading file: {e}"


def download_button_example(url):
    """
    Example function to demonstrate the download button with a provided URL.
    """
    filename = "downloaded_file.txt" # Fixed filename for demonstration

    filepath = save_url_to_cache(url, filename)

    if "Error" in filepath:
        return filepath # Return error message
    
    return filepath # Return path to the downloaded file, which will trigger Gradio to offer it as a download


if __name__ == "__main__":
    with gr.Blocks() as demo:
        url_input = gr.Textbox(label="Enter URL to download")
        download_output = gr.Textbox(label="Downloaded File Path")
        download_button = gr.Button("Download")

        download_button.click(fn=download_button_example, inputs=url_input, outputs=download_output)

    demo.launch()

Patched code sample

import urllib.parse
import requests
import os

def save_url_to_cache_fixed(url, cache_dir="."):
    """
    Downloads a file from a URL and saves it to a local cache,
    with SSRF protection.
    """
    try:
        parsed_url = urllib.parse.urlparse(url)

        # SSRF Protection: Check allowed schemes and domains
        allowed_schemes = ["http", "https"]
        allowed_domains = ["example.com", "example.org", "gradio.app"] # Replace with your allowed domains

        if parsed_url.scheme not in allowed_schemes:
            raise ValueError(f"Invalid scheme: {parsed_url.scheme}. Allowed schemes are: {allowed_schemes}")

        if parsed_url.hostname not in allowed_domains:
            raise ValueError(f"Invalid domain: {parsed_url.hostname}. Allowed domains are: {allowed_domains}")

        # Further validation - prevent file:///  or other local access schemes slipping through.
        if parsed_url.scheme not in ('http', 'https'):
            raise ValueError("Only http or https URLs are allowed.")

        # Prevent path traversal attacks.  Crucially important.
        # This is an extra layer on top of scheme/domain validation.
        if ".." in parsed_url.path:
            raise ValueError("Path traversal detected.")


        response = requests.get(url, stream=True)
        response.raise_for_status()  # Raise HTTPError for bad responses (4xx or 5xx)

        filename = os.path.basename(parsed_url.path)
        filepath = os.path.join(cache_dir, filename)

        with open(filepath, "wb") as f:
            for chunk in response.iter_content(chunk_size=8192):
                f.write(chunk)

        return filepath
    except Exception as e:
        print(f"Error downloading {url}: {e}")
        return None

if __name__ == '__main__':
    # Example usage
    # Replace with a URL that *should* be allowed based on the filters above
    allowed_url = "https://example.com/somefile.txt"
    cached_file = save_url_to_cache_fixed(allowed_url)
    if cached_file:
        print(f"File downloaded and cached at: {cached_file}")

    # Example of a blocked URL (SSRF protection)
    blocked_url = "file:///etc/passwd"  # Simulate SSRF attempt
    cached_file = save_url_to_cache_fixed(blocked_url) # This should throw error because of file:/// scheme

    # Example of a blocked URL (SSRF protection, different domain)
    blocked_url = "https://malicious.com/somefile.txt"  # Simulate SSRF attempt
    cached_file = save_url_to_cache_fixed(blocked_url) # This should throw error because of unallowed domain

    # Example of a blocked URL (Path traversal protection)
    blocked_url = "https://example.com/../../etc/passwd"
    cached_file = save_url_to_cache_fixed(blocked_url)

Payload

`file:///etc/passwd`

Cite this entry

@misc{vaitp:cve202448052,
  title        = {{Gradio SSRF in DownloadButton allows local resource access.
}},
  author       = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
  year         = {2024},
  note         = {VAITP Python Vulnerability Dataset, entry CVE-2024-48052},
  howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2024-48052/}}
}
Introducing the "VAITP dataset": a specialized repository of Python vulnerabilities and patches, meticulously compiled for the use of the security research community. As Python's prominence grows, understanding and addressing potential security vulnerabilities become crucial. Crafted by and for the cybersecurity community, this dataset offers a valuable resource for researchers, analysts, and developers to analyze and mitigate the security risks associated with Python. Through the comprehensive exploration of vulnerabilities and corresponding patches, the VAITP dataset fosters a safer and more resilient Python ecosystem, encouraging collaborative advancements in programming security.

The supreme art of war is to subdue the enemy without fighting.

Sun Tzu – “The Art of War”

:: Shaping the future through research and ingenuity ::