VAITP Dataset

← Back to the dataset

CVE-2026-35187

Authenticated SSRF in pyLoad allows internal network and local file access.

  • CVSS 7.7
  • CWE-918
  • Input Validation and Sanitization
  • Remote

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).

CVSS base score
7.7
Published
2026-04-06
OWASP
A10 Server-Side Request Forgery (SSRF)
Orthogonal defect classification
Checking
Code defect classification
Missing Check
Category
Input Validation and Sanitization
Subcategory
Server-Side Request Forgery (SSRF)
Accessibility scope
Remote
Impact
Unauthorized Access
Affected component
pyLoad
Fixed by upgrading
Yes

Solution

No official patched version is available. The recommended solution is to manually apply the patch from pull request #3536 in the official GitHub repository to validate URL schemes.

Vulnerable code sample

import urllib.request
import urllib.error

# This is a simplified representation of the utility function that makes the web request.
# In the actual pyLoad source, this uses pycurl, which also supports protocols
# like file://, gopher://, etc., leading to the vulnerability.
def get_url(url, post=None):
    """
    Fetches the content of a given URL.
    No validation is performed on the URL, its protocol, or the target IP address.
    """
    try:
        # urllib.request.urlopen, like pycurl, can handle various protocols
        # including file://, which is a key part of the vulnerability.
        with urllib.request.urlopen(url) as response:
            return response.read().decode('utf-8', errors='ignore')
    except urllib.error.URLError as e:
        # The original vulnerability allowed for error-based discovery.
        # Returning the error helps represent this aspect.
        return f"Error: {e}"


class Api:
    """
    A representative class mimicking the vulnerable part of the pyLoad API.
    """

    def __init__(self):
        """
        In the real application, self.core would be initialized with the
        main pyLoad core object.
        """
        # This is a placeholder for demonstration purposes.
        self.core = type('Core', (object,), {
            'package_manager': type('PackageManager', (object,), {
                'add_package': lambda name, data, pid: (name, data, pid)
            })()
        })()

    def parse_urls(self, urls, password=""):
        """
        Parse urls for packages. This is the vulnerable function.
        It takes a URL from an authenticated user and fetches its content
        without any validation.
        """
        # THE VULNERABILITY:
        # The user-provided `urls` variable is passed directly to a function
        # that makes a server-side request. There is no check to ensure the
        # URL is safe, does not point to an internal resource, or uses a
        # restricted protocol.
        data = get_url(urls, post={"pass": password})

        # The fetched data is then processed, completing the SSRF chain.
        return self.core.package_manager.add_package(
            "Crawled from %s" % urls, data, -1
        )

Patched code sample

def parse_urls(self, urls, password=""):
    """
    Parse urls and returns a list of packages
    """
    # SSRF check, see issue #3682 and CVE-2023-0297
    # This 'if' block is the fix. It validates that the URL scheme is either
    # http or https before it is passed to the function that makes the request.
    # This prevents the use of potentially dangerous schemes like file://, gopher://, etc.
    if not urls.lstrip().startswith(("http://", "https://")):
        raise Exception("URL scheme not allowed")

    # The line below was the source of the vulnerability. It is now only
    # executed after the URL scheme has been validated by the block above.
    # The 'get_url' function (not shown) is responsible for fetching the URL content.
    urls = get_url(urls, post={"p": password})

    # The rest of the function, which parses the content returned by get_url,
    # is omitted as it is not relevant to the security fix.
    pass

Payload

file:///etc/passwd

Cite this entry

@misc{vaitp:cve202635187,
  title        = {{Authenticated SSRF in pyLoad allows internal network and local file access.}},
  author       = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
  year         = {2026},
  note         = {VAITP Python Vulnerability Dataset, entry CVE-2026-35187},
  howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2026-35187/}}
}
Introducing the "VAITP dataset": a specialized repository of Python vulnerabilities and patches, meticulously compiled for the use of the security research community. As Python's prominence grows, understanding and addressing potential security vulnerabilities become crucial. Crafted by and for the cybersecurity community, this dataset offers a valuable resource for researchers, analysts, and developers to analyze and mitigate the security risks associated with Python. Through the comprehensive exploration of vulnerabilities and corresponding patches, the VAITP dataset fosters a safer and more resilient Python ecosystem, encouraging collaborative advancements in programming security.

The supreme art of war is to subdue the enemy without fighting.

Sun Tzu – “The Art of War”

:: Shaping the future through research and ingenuity ::