# OpenC3 COSMOS: Authenticated OS command injection via the `pypi_url` setting

Authenticated user injects shell metacharacters into the pypi_url setting, which is interpolated into a backtick pip install command, yielding OS command execution as openc3.

- Payout: Research
- Severity: high
- Program: OpenC3 COSMOS
- Found by: Marnick39
- Disclosed: 2026-09-23
- CVE: CVE-2026-77601
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-vp3w-52v9-q57f
- Library page: https://bugbountylibrary.com/r/ghsa%3AGHSA-vp3w-52v9-q57f
- Tags: area:web domain:api domain:web impact:internal-access impact:rce kind:research platform:ghsa sev:high tech:ruby technique:source-review vuln:auth-bypass vuln:command-injection vuln:rce

**Target:** openc3-cosmos-cmd-tlm-api JSON-RPC set_setting and plugin install
**Root cause:** PluginModel.install_phase2 interpolates the unvalidated pypi_url setting into a pip argument string executed via Ruby backticks (/bin/sh -c), so shell metacharacters run. The sibling python_package_model.rb uses an argv array and is not injectable.
**Steps:**
1. Obtain session token: `POST /openc3-api/auth/verify {"password":"<password>"}`
2. Write setting: `POST /openc3-api/api {"jsonrpc":"2.0","method":"set_setting","params":["pypi_url","https://pypi.org ; id > /tmp/A1_PWNED 2>&1 ; #"],"keyword_params":{"scope":"DEFAULT"},"id":1}`
3. Upload plugin gem containing a `requirements.txt`: `POST /openc3-api/plugins` (multipart plugin=@malicious.gem, scope=DEFAULT)
4. Install it: `POST /openc3-api/plugins/install/<id>` with plugin_hash and scope=DEFAULT
5. Injected command runs in install process; marker file contains `uid=1001(openc3)`
**How found:** Code review of plugin install path; noticed plugin_model.rb:288 uses a backtick while the sibling python_package_model.rb:95 uses ProcessManager.spawn with an argv array, flagging the shell-interpolated site.
**Exploitation:** Any authenticated user (open-source edition: authorize only checks token validity, ignores permission:) writes pypi_url with a trailing `#` to comment out the appended `/simple` suffix, uploads a plugin gem with requirements.txt to trigger the python install branch, and installs it; the backtick shell executes the injected command as uid 1001 inside the cmd-tlm-api container.
**Escalation:** Command execution in cmd-tlm-api exposes the Redis/Valkey password and S3 bucket credentials and operates across all scopes, enabling telemetry access, commanding, and tampering with any scope.
**Insight:** When a codebase has two near-identical installer paths, diff them: the one using argv arrays is safe, the one using backticks/system with string interpolation is the injection site. Also note trailing `#` comments out appended suffixes like `/simple`.

