CVE-2026-77602: OpenC3 COSMOS: Authenticated remote code execution via the user-writable config overlay (table definitions, cmd/tlm definitions, and script suites)

Published Sep 23, 2026
·
Updated

Summary

COSMOS reads configuration from a user-writable overlay (targetsmodified/) before the read-only plugin-installed targets/ tree, and the config subsystem executes code on those files: ConfigParser renders every file as ERB by default, a GENERICREADCONVERSION / GENERICWRITECONVERSION block is evaluated as code by GenericConversion (Ruby and Python), and the Script Runner suite analysis requires a procedure file. An authenticated user can write into targetsmodified/ below the admin tier (the storage-upload endpoint exempts that area from the admin gate, and the screen-save endpoint stores its body verbatim there), so the same root cause is reachable through several features, each giving arbitrary code execution on a COSMOS server.

Three vulnerable routes were identified, all reachable by an authenticated non-admin user (in the open-source edition authorize ignores the permission string, so any authenticated user qualifies):

1. Table definitions (immediate). tables#generate|report|load reads a definition from targetsmodified/ and ERB-renders it and evaluates its GENERICCONVERSION block in the cmd-tlm-api container. 2. Command/telemetry definitions (persistent). A file written to targetsmodified/<TARGET>/cmdtlm/ is overlaid by System.setuptargets and processed by PacketConfig in the decom/multi microservices: ERB-rendered in the Ruby implementation, and GENERIC-evaluated in both the Ruby and Python implementations (the Python ConfigParser does not run ERB). It executes on the next microservice (re)start. 3. Script Runner suites (immediate). A procedure written to targetsmodified/<TARGET>/procedures/ is required by the suite analysis, reachable at the read-only scriptview tier through scripts#body and runningscript#show (the analysis subprocess is spawned when OPENC3SERVICEPASSWORD is configured, which it is in the shipped .env).

Details

Root cause. TargetFile.body (Ruby openc3/lib/openc3/utilities/targetfile.rb, Python openc3/python/openc3/utilities/targetfile.py) reads {scope}/targetsmodified/{name} before {scope}/targets/{name}. The storage-upload endpoint storagecontroller.rb getuploadpresignedrequest is gated at systemset and exempts targetsmodified/ and tmp/ from its admin check (so a write there is not admin-gated); for a target path it additionally calls authorizebucketpath, which in the permission-enforcing edition requires tlm on the target, while in the open-source edition authorize ignores the permission string entirely. screenscontroller.rb create (systemset) stores its request body verbatim under targetsmodified/<target>/screens/. So a non-admin can place files in the overlay. The config subsystem then executes them.

Sink 1, ERB. ConfigParser#parsefile renders the file as ERB before parsing (runerb defaults to true): ruby openc3/lib/openc3/config/configparser.rb:402 output = ERB.new(File.read(filename)...commenterb(), trimmode: "-").result(...) Reached for table definitions via tablescontroller.rb -> Table.getdefinitions -> TableConfig.processfile -> parsefile, and for cmd/tlm definitions via System.setuptargets (system.rb, whose overlay loop copies targetsmodified/<T>/cmdtlm/ over the read-only files) -> PacketConfig#processfile -> parsefile.

Sink 2, GENERIC conversion. PacketConfig/TableConfig build a GenericConversion from a GENERICREADCONVERSIONSTART .. END / GENERICWRITECONVERSIONSTART .. END block, and GenericConversion#call evaluates it (independent of ERB): ruby openc3/lib/openc3/conversions/genericconversion.rb (call) eval(@codetoeval) Python openc3/python/openc3/conversions/genericconversion.py (call): compile()/exec()/eval() The read conversion fires on tables#report/load and during telemetry decom; the write conversion fires on tables#generate and on restoredefaults.

Sink 3, suite require. The Script Runner suite analysis executes the file: ruby openc3-cosmos-script-runner-api/scripts/runsuiteanalysis.rb:24 require ARGV[1] # runs all top-level code of the supplied file reached from Script.processsuite, invoked by scripts#body and runningscript#show (both scriptview) and scripts#create (scriptedit) when the file matches the suite pattern.

Permission tiers. Writing the payload needs systemset (screen save, storage upload) or scriptedit (script create); triggering needs system (tables) or scriptview (suite). These are below the tiers where COSMOS gates code execution elsewhere (plugin install requires admin, running a script requires scriptrun).

PoC

Table definition path, against a standard stack. Benign payload writes id to a marker file. This PoC uses the open-source password login; in the permission-enforcing edition substitute a bearer token for a user holding the permissions noted above. bash BASE=http://localhost:2900/openc3-api # adjust to your deployment TOKEN=$(curl -s -X POST "$BASE/auth/verify" -H 'Content-Type: application/json' -d '{"password":"<your password>"}')

1) Write the payload into targetsmodified/ via the screen save endpoint. curl -s -X POST "$BASE/screen" -H "Authorization: $TOKEN" \ --data-urlencode 'scope=DEFAULT' --data-urlencode 'target=INST' --data-urlencode 'screen=poc' \ --data-urlencode $'text=SCREEN AUTO AUTO 1.0\n<%= File.write("/tmp/erbrcepoc", id) %>\nLABEL poc'

2) Trigger by pointing a table action at that file. curl -s -X POST "$BASE/tables/generate" -H "Authorization: $TOKEN" \ --data-urlencode 'scope=DEFAULT' --data-urlencode 'definition=INST/screens/poc.txt' Then in the cmd-tlm-api container: cat /tmp/erbrcepoc shows uid=1001(openc3) .... The tables/generate request returns HTTP 500 (the screen lines are not valid table keywords); the marker shows the code already ran.

The same outcome without ERB, using the GENERIC sink, on the same tables/generate trigger: TABLE "data" BIGENDIAN KEYVALUE "poc" APPENDPARAMETER "item1" 8 UINT 0 255 0 "Item" GENERICWRITECONVERSIONSTART id > /tmp/erbrcepoc 0 GENERICWRITECONVERSIONEND

cmd/tlm path: upload a telemetry definition containing the same ERB or GENERIC block to targetsmodified/<TARGET>/cmdtlm/<file>.txt via the storage-upload presigned request (systemset), then the code runs in that target's decom microservice on its next restart. Suite path: write a suite-shaped procedure to targetsmodified/<TARGET>/procedures/<x>.rb and call scripts#body on it at scriptview.

The ERB table chain was confirmed end to end over HTTP against a booted Rails and puma instance.

Impact

Arbitrary code execution as the openc3 user in the cmd-tlm-api container and the per-target decom microservices and the script-runner. Those processes hold the Redis and bucket credentials and sit on the internal service network, so the executed code acts with that authority over configuration, telemetry, and command data across scopes. The API is served through Traefik, which the shipped compose binds to 127.0.0.1:2900, so a default single-host install is reachable only from the host; a multi-user deployment exposes the web port, and the AV:N rating reflects that standard remote-operator exposure.

All paths require valid authentication, and the triggering permissions (system/systemset/scriptview) are below the admin/scriptrun/plugin-install tiers where COSMOS gates code execution. In the open-source edition authorize checks only token validity and does not enforce the permission string, so any authenticated user can perform these requests.

Suggested fix

The fix is to treat the user-writable overlay as data, never code, and to gate the writers, applied uniformly: - Load table and cmd/tlm definitions for code-execution paths from the read-only targets/ tree only, or parse the targetsmodified overlay with ERB disabled (runerb=false); dynamically-created packet definitions are structural and never need ERB, so this does not regress that feature. - Allow only admin and the server-side dynamic-packet mechanism to write a cmdtlm overlay; reject non-canonical object keys so a positional path check cannot be bypassed by a key the object store normalizes differently. - Run the Script Runner suite analysis (which executes the file) only at the scriptrun tier, at every entry point. - Mirror the definition-read change in the Python implementation.

Other sources

OpenC3 COSMOS provides the functionality needed to send commands to and receive data from one or more embedded systems. From 5.1.0 until 7.3.0, authenticated non-administrator users can write content under targetsmodified/ that is later executed by multiple configuration paths below the intended code-execution privilege tier. Table and command or telemetry definitions are processed through ConfigParser, PacketConfig, GENERICREADCONVERSION, or GENERICWRITECONVERSION, allowing ERB rendering or Ruby and Python evaluation, while openc3-cosmos-script-runner-api/scripts/runsuiteanalysis.rb executes suite procedure files through require. Storage uploads, screen saves, and script creation can place content in the overlay, and triggering table processing, a cmd/tlm reload, or suite analysis executes the content in cmd-tlm-api, decom microservices, or Script Runner with access to internal credentials and data. This issue is fixed in version 7.3.0.

MITRE

Affected Software

2 affected componentsFixes available
OpenC3 COSMOS>=5.1.0<7.3.0
rubygems/openc3>=5.1.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. Upgrade

    Upgrade OpenC3 COSMOS to a version that resolves this vulnerability.

    Fixed in 7.3.0
  3. Configuration

    Allow only administrators and the server-side dynamic-packet mechanism to write a cmd_tlm overlay, and reject non-canonical object keys so positional path checks cannot be bypassed.

    targets_modified overlay writers write authorization and object-key validation = admin or server-side dynamic-packet mechanism only; reject non-canonical object keys
  4. Configuration

    Load code-execution-path table and cmd/tlm definitions only from the read-only targets/ tree, or parse targets_modified/ overlays with run_erb=false. Apply the same definition-read behavior in both Ruby and Python implementations.

    table and cmd/tlm definition loading definition source and run_erb = read-only targets/ only, or run_erb=false for targets_modified/
  5. Configuration

    Run suite analysis, which executes the procedure file, only at the script_run tier and enforce this requirement at every entry point.

    Script Runner suite analysis execution permission tier = script_run

Event History

Sep 23, 2026
CVE Published
via MITRE·06:56 PM
Data Sourced
via MITRE·06:56 PM
DescriptionSeverityWeakness
Advisory Published
via GitHub·06:57 PM
Data Sourced
via GitHub·06:57 PM
DescriptionSeverityWeaknessAffected Software
Data Sourced
via NVD·07:19 PM
DescriptionSeverityWeakness

Frequently Asked Questions

1

Which deployments and users are exposed?

OpenC3 COSMOS versions from 5.1.0 until 7.3.0 are affected. An authenticated non-administrator user can exploit the issue; administrator privileges are not required.

2

What does an attacker need to do to execute code?

The attacker needs authenticated access and the ability to place crafted content under targets_modified/, including through storage uploads, screen saves, or script creation. Code execution occurs when the affected content is later processed through table processing, a command/telemetry reload, or suite analysis.

3

Which components may execute the injected content?

Injected content may be executed by cmd-tlm-api, decom microservices, or Script Runner. These components can access internal credentials and data.

4

What should be done to remediate the issue?

Upgrade OpenC3 COSMOS to version 7.3.0, which fixes the issue.

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