Feb 20, 2026·6 min read·3 visits
OpenC3 COSMOS, a command and control system for satellites, contains a critical 10.0 CVSS vulnerability. It uses Ruby's `eval()` to parse array-like strings in API requests. Because this parsing happens before authentication, anyone can send a crafted JSON-RPC packet to execute code as the server user. Patch immediately to version 6.10.2.
A critical unauthenticated Remote Code Execution (RCE) vulnerability in OpenC3 COSMOS allows attackers to execute arbitrary commands on the host server. The flaw stems from the insecure usage of Ruby's `eval()` method during the parsing of JSON-RPC parameters, specifically within the `cmd` method handler. Crucially, parameter parsing occurs before authentication checks, allowing unprivileged attackers to gain full control over the satellite command and control system.
OpenC3 COSMOS is the kind of software that makes a security researcher's ears perk up. It isn't just another CRUD app; it is 'Command and Control' software designed for embedded systems, test equipment, and yes, satellites. It is the digital nervous system for mission-critical hardware. When you are controlling hardware that moves at 17,000 miles per hour, the stakes are naturally higher than your average e-commerce store.
So, when a vulnerability drops in OpenC3 with a CVSS score of a perfect 10.0, it is not a drill. CVE-2025-68271 is an unauthenticated Remote Code Execution (RCE) flaw. In plain English, this means an attacker can walk up to the mission control dashboard's API, whisper a few magic words, and instantly gain the privileges of the user running the server. No password, no token, no invite required.
What makes this specific vulnerability fascinating—and terrifying—is how pedestrian the root cause is. It isn't a complex heap overflow or a race condition in a kernel driver. It is a textbook failure in handling user input in a dynamic language. It is the digital equivalent of leaving the keys to the Space Shuttle under the doormat.
To understand this bug, you have to look at how Ruby developers sometimes solve the problem of 'Type Juggling.' The application receives data via a JSON-RPC API. Sometimes that data comes in as a string, but the application really wants it to be an Array. A safe way to do this would be to use a JSON parser. The unsafe way—the 'hold my beer' way—is to use eval().
Deep inside openc3/lib/openc3/core_ext/string.rb, the developers extended the native String class with a helper method called convert_to_value. This method acts as a heuristic scanner. It looks at a string and guesses what it should be. If the string is wrapped in square brackets, like "[1, 2, 3]", the code assumes it is an array.
Here is the fatal mistake: rather than parsing that string safely, the code simply passed the entire string to Ruby's eval() function. eval() executes the string as valid Ruby code. This is the ultimate sin in dynamic languages. If an attacker controls the string going into eval(), they control the CPU.
Let's look at the smoking gun. This is the code responsible for the vulnerability. It is a classic example of convenience over security.
Vulnerable Code (Pre-Patch):
# openc3/lib/openc3/core_ext/string.rb
def convert_to_value
if self.is_int?
return to_i
elsif self.is_float?
return to_f
elsif self.is_array? # Checks if string starts with [
return eval(self) # <--- GAME OVER
else
return self
end
endThe is_array? check is merely a regex looking for square brackets. It doesn't validate the contents of the brackets. So, if I send "[ system('rm -rf /') ]", the application sees the brackets, says 'Looks like an array to me!', and hands it to eval.
The fix involves swapping the dangerous eval for a safe serializer. In the patched version (6.10.2), they switched to YAML.safe_load, which parses the structure without executing code.
Fixed Code (Post-Patch):
# openc3/lib/openc3/core_ext/string.rb
elsif self.is_array? or self.is_object?
return YAML.safe_load(self)
endYou might be asking: 'But surely I need to be logged in to send commands to a satellite controller?' That would be logical. However, logic often takes a backseat to execution flow bugs. The vulnerability exists in the processing of the request parameters, which happens before the authorization middleware kicks in.
The attack vector targets the JSON-RPC endpoint. When the server receives a POST request, it attempts to route the command. Specifically, the cmd method requires parameters to be parsed so the system knows which target packet to construct. The server triggers convert_to_value on the attacker's input in order to figure out what the input is.
The Attack Chain:
/api/rpc.cmd and the params to ["#{ id }"].eval().id via backticks, and substitutes the result.401 Unauthorized.Here is what that flow looks like visually:
The impact here is total system compromise (CVSS 10.0). Since OpenC3 often runs with elevated privileges to interface with hardware drivers or serial ports, the attacker likely inherits those permissions.
In a best-case scenario, the attacker installs a crypto miner on your ground station server. In a worst-case scenario, they can inject commands into the telemetry stream. If this system is controlling a thermal vacuum chamber, a robotic arm, or an actual satellite link, the physical consequences could be catastrophic.
Furthermore, because the exploit leaves very little trace other than a 'failed' login attempt in the logs, it is an ideal candidate for stealthy persistence. An attacker could patch the vulnerability themselves after gaining entry to lock out other hackers, leaving the admins none the wiser.
If you are running OpenC3 COSMOS versions 5.0.0 through 6.10.1, you are sitting on a powder keg. The remediation is straightforward: Update to version 6.10.2 immediately.
If you cannot update right now (perhaps you are in the middle of a critical mission), you must implement WAF rules to block this attack vector. You specifically want to filter JSON-RPC traffic containing the cmd method where parameters contain Ruby interpolation syntax (#{) or backticks.
However, WAF rules are band-aids. The real fix is ensuring that user input is never treated as executable code. This vulnerability serves as a stark reminder: eval is not a parsing library. If you need to deserialize data, use a deserializer. If you need to run code, rethink your architecture.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H| Product | Affected Versions | Fixed Version |
|---|---|---|
OpenC3 COSMOS OpenC3 | >= 5.0.0, <= 6.10.1 | 6.10.2 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-95 (Eval Injection) |
| CVSS v3.1 | 10.0 (Critical) |
| Attack Vector | Network (Pre-Auth) |
| Impact | Remote Code Execution (RCE) |
| Vulnerable Method | String#convert_to_value |
| Sink | Kernel.eval() |
The product receives input from an upstream component, but it does not neutralize or incorrectly neutralizes code syntax before using the input in a dynamic evaluation call (e.g. 'eval').