Apr 23, 2026·7 min read·5 visits
A logic flaw in OpenC3 COSMOS allows an attacker with a hijacked session token to supply the token as the current password during a password reset request. The system incorrectly validates the token as a valid credential, allowing the attacker to change the account password and establish persistent access.
OpenC3 COSMOS contains a critical authentication vulnerability where session tokens and user passwords are treated interchangeably by the internal credential verification mechanism. An attacker possessing a valid session token can use that token to bypass the "old password" requirement during a password reset operation. This leads to persistent account takeover and locks the legitimate user out of the affected account.
OpenC3 COSMOS operates as a command and control system, requiring strict authentication and authorization boundaries for operators. The system utilizes the AuthModel component to manage user credentials, session caching, and password lifecycle events. The vulnerability exists within this component, specifically categorized under CWE-287 (Improper Authentication).
The core issue stems from an architectural oversight in how the system processes credential verification requests. The authentication verification method fails to differentiate between a session token and a plaintext password when processing authorization requests for sensitive operations. The system treats these two distinct credential types interchangeably.
Because the password reset endpoint leverages this generalized verification method to authenticate the "old password" parameter, an attacker can substitute the required plaintext password with an active session token. The system validates the token against the active session cache, returns a successful verification status, and permits the password modification.
The impact is direct and measurable. An attacker who obtains temporary access via a hijacked session token can escalate this ephemeral access into permanent account control. By changing the account password, the attacker establishes persistence, locks the legitimate user out of the system, and retains access even after the original stolen session token expires.
The root cause of this vulnerability lies in the implementation of the OpenC3::AuthModel.verify_no_service method within the openc3/lib/openc3/models/auth_model.rb file. This method serves as the primary gateway for validating user credentials against internal data stores.
Prior to the patch, the method accepted two parameters: a token string and a no_password boolean flag. The token parameter was designed to handle either a session token or a plaintext password. The method execution path first verified if the provided string matched an active entry in the internal session cache. If a match was found, the method immediately returned a successful authorization response.
If the initial token check failed and the no_password flag was set to false, the method proceeded to evaluate the input against the stored Argon2 password hash. This sequential fallback logic meant that any valid session token would successfully pass the verification check before the password hash comparison ever executed.
The secondary component of the root cause exists in the AuthModel.set method, which handles password modifications. This method invoked verify_no_service(old_password, no_password: false) to authenticate the user before applying the new password. Because the verification method returned true for valid session tokens, the password modification routine accepted the session token as a valid "old password" credential.
A detailed review of the code paths in openc3/lib/openc3/models/auth_model.rb reveals the structural flaw in the credential validation logic. The vulnerable version of the verify_no_service method processes the input sequentially.
# Vulnerable logic snippet
def self.verify_no_service(token, no_password: true)
return false if token.nil? or token.empty?
time = Time.now
return true if @@session_cache and (time - @@session_cache_time) < SESSION_CACHE_TIMEOUT and @@session_cache[token]
unless no_password
return true if @@pw_hash_cache and (time - @@pw_hash_cache_time) < PW_HASH_CACHE_TIMEOUT and Argon2::Password.verify_password(token, @@pw_hash_cache)
end
# ... further checks ...
endIn this implementation, the @@session_cache[token] check executes first. If an attacker provides a valid session token via the old_password field during a password reset, the condition evaluates to true, bypassing the subsequent Argon2::Password.verify_password check entirely.
The patch introduced in commit 2e623714e3426d5ae81b6f8239d4a2a6937ef776 resolves this flaw by replacing the binary no_password flag with a granular mode parameter. This parameter explicitly defines the type of credential being verified.
# Patched logic snippet
def self.verify_no_service(token, mode: :any)
return false if token.nil? or token.empty?
time = Time.now
if [:any, :token].include?(mode)
return true if @@session_cache and (time - @@session_cache_time) < SESSION_CACHE_TIMEOUT and @@session_cache[token]
end
if [:any, :password].include?(mode)
return true if @@pw_hash_cache and (time - @@pw_hash_cache_time) < PW_HASH_CACHE_TIMEOUT and Argon2::Password.verify_password(token, @@pw_hash_cache)
end
# ... further checks ...
endAlongside this change, the AuthModel.set routine was updated to enforce mode: :password when verifying the old_password parameter. This explicit requirement prevents the session cache evaluation block from executing during password modifications, ensuring that only the cryptographic hash validation path is taken.
Exploitation of this vulnerability requires the attacker to possess a valid, active user session token. This token must be obtained through an independent vector, such as cross-site scripting (XSS), network interception, or physical access to an authenticated workstation.
Once the session token is acquired, the attacker initiates a password reset operation by sending an HTTP request to the /auth/set endpoint. The attacker constructs the request payload by injecting the stolen session token into the old_password field, while supplying their desired new password in the new_password field.
The COSMOS API receives the request and forwards the payload to the AuthModel. The system evaluates the old_password string against the active session cache. Since the attacker provided a valid token, the cache lookup succeeds, and authorization is granted to modify the user record.
The database commits the new password hash, effectively completing the attack. The attacker can now authenticate to the system using the username and the newly established password, operating independently of the initial stolen token.
The exploitation of GHSA-WGX6-G857-JJF7 leads to complete account takeover. The attacker transitions from holding a temporary session identifier to possessing the permanent credentials required to authenticate at will. This transition neutralizes the effectiveness of session timeouts and token revocation mechanisms.
By executing the password reset, the attacker simultaneously enforces a denial-of-service condition against the legitimate user. The user is locked out of their account and cannot re-authenticate to remediate the breach. In environments where operators share a small pool of accounts, this locks out the entire operational team until administrative intervention occurs.
Furthermore, the severity of the impact depends on the privileges assigned to the compromised account. If the hijacked session belongs to an administrator or a privileged operator, the attacker gains full control over the OpenC3 COSMOS deployment. This level of access permits arbitrary command execution against connected target systems, modification of telemetry data, and disruption of mission operations.
The primary remediation for this vulnerability is applying the vendor-supplied patches. Organizations must upgrade their OpenC3 COSMOS deployments to version 6.10.5 or version 7.0.0-rc3. These releases contain the necessary code changes to enforce strict credential type checking during authentication events.
Following the upgrade process, security teams should mandate a global password reset for all operator accounts. This action ensures that any account potentially compromised via this vulnerability prior to patching is restored to a known good state. In conjunction with password resets, all active sessions should be invalidated to force re-authentication.
To detect historical exploitation attempts, security engineers should review system authentication logs. Analysts should query the logs for password change events and examine the length and format of the recorded old_password strings, if captured. While most systems redact plaintext passwords in logs, instances where debugging was enabled might reveal high-entropy, token-length strings being supplied as the old password, indicating successful exploitation.
| Product | Affected Versions | Fixed Version |
|---|---|---|
OpenC3 COSMOS OpenC3 | < 6.10.5 | 6.10.5 |
OpenC3 COSMOS OpenC3 | >= 7.0.0.pre.rc1, < 7.0.0-rc3 | 7.0.0-rc3 |
| Attribute | Detail |
|---|---|
| Vulnerability Type | Improper Authentication |
| CWE ID | CWE-287 |
| Attack Vector | Network (Requires Hijacked Session) |
| Impact | Account Takeover / Privilege Escalation |
| Exploit Status | Proof of Concept Required |
| CVSS Score | 8.1 |
Software does not prove or insufficiently proves that a claim to be a specific user or actor is correct.