GitHub Advisoryadvisory · Maven
GHSA-3g44-3m7x-cgg2 Yamcs vulnerable to authenticated RCE via StreamSQL aggregate-compiler column-name injection in Yamcs `executeSql`
CRITICAL
Official page ↗
- Aliases
- CVE-2026-55511
- Ecosystem
- Maven
- Packages
- org.yamcs:yamcs-core
- Severity
- Critical
- CVSS vector
- CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:C/C:H/I:H/A:H
- Weaknesses
- CWE-94
- Reviewed
- 2026-08-28
Affected packages
• Maven org.yamcs:yamcs-core: >= 5.13.0, < 5.13.2 → fixed in 5.13.2
• Maven org.yamcs:yamcs-core: < 5.12.8 → fixed in 5.12.8
Description
Overview
Yamcs compiles StreamSQL expressions to Java on the fly with the Janino `SimpleCompiler` (no restrictive class-loading policy or expression sandbox). When a StreamSQL aggregate such as `sum(...)` is applied to a column, the column's *name* is interpolated unescaped into the generated Java source. Because Yamcs accepts arbitrary characters in a double-quoted column identifier and applies no validation when a column is created, an authenticated user with the `ControlArchiving` system privilege can craft a column name that injects arbitrary Java into the compiled aggregate and achieve Remote Code Execution on the Yamcs host via `POST /api/archive/{instance}:executeSql`.
This is a second, independent Janino-RCE entry point sharing the root cause of CVE-2026-44632 (GHSA-524g-x36v-9wm6). The 5.13.0 / 5.12.7 fix for CVE-2026-44632 hardened only the algorithm-override path (`JavaExprAlgorithmExecutionFactory`, reached via `MdbOverrideApi` and gated by `ChangeMissionDatabase`). The StreamSQL expression compiler (`org.yamcs.yarch.streamsql`) was not addressed and remains exploitable in 5.13.0 via a different privilege (`ControlArchiving`).
Impact
An authenticated Yamcs user holding `SystemPrivilege.ControlArchiving` can cause Yamcs to compile attacker-controlled Java source through the StreamSQL aggregate expression compiler reachable from `POST /api/archive/{instance}:executeSql`.
The injected code executes inside the Yamcs server JVM with the privileges of the Yamcs server process, bypassing the Yamcs authorization model. Because this is ordinary Java execution, dangerous JDK APIs such as filesystem access, process execution, reflection, and class loading are reachable unless externally sandboxed. This allows compromise of confidentiality, integrity, and availability of the Yamcs deployment: access to mission data and credentials available to the process, telemetry/archive tampering, denial of service, and lateral movement from the host environment.
`ControlArchiving` is the archive/table/stream management privilege — distinct from both superuser access and the `ChangeMissionDatabase` privilege used by the previously fixed algorithm-override Janino issue (CVE-2026-44632). Scope is scored as Changed because execution crosses from an authenticated Yamcs API privilege into arbitrary code execution under the server process / host OS authority, outside the privileges granted to the authenticated Yamcs user (consistent with the maintainer's S:C scoring of the sibling CVE-2026-44632).
Technical Details
The sink: unsandboxed Janino compilation of generated Java
`org.yamcs.yarch.streamsql.CompilableAggregateExpression#getCompiledAggregate()` builds a Java source string and compiles it with Janino, with no restrictive ClassLoader and no class/API allowlist:
```java
// org/yamcs/yarch/streamsql/CompilableAggregateExpression.java:26-50
String className = "AggregateExpression" + counter.incrementAndGet();
StringBuilder code = new StringBuilder();
code.append("package org.yamcs.yarch;\n")
.append("public class " + className + " implements CompiledAggregateExpression {\n");
aggregateFillCode_Declarations(code);
code.append("\tpublic void newData(Tuple tuple) {\n");
aggregateFillCode_newData(code); // `:
```java
// org/yamcs/yarch/streamsql/funct/SumExpression.java:38-42
protected void aggregateFillCode_newData(StringBuilder code) throws StreamSqlException {
fillCode_InputDefVars(inputDef.getColumnDefinitions(), code); // emits the column declaration
code.append("\t\tsum+=col" + children[0].getColumnName()); // emits the column identifier again
code.append(";\n");
}
```
`fillCode_InputDefVars` interpolates the column name into a Java identifier with only `sanitizeName` applied, and again — raw — into a `tuple.getColumn("...")` string literal:
```java
// org/yamcs/yarch/streamsql/Expression.java:134-145
String javaColIdentifier = "col" + sanitizeName(cd.getName());
...
code.append("\t\t" + dtype.javaType() + " " + javaColIdentifier +
" = (" + dtype.javaType() + ")tuple.getColumn(\"" + cd.getName() + "\");\n");
// Expression.java:237-238 — the ONLY transformation applied to the column name:
static String sanitizeName(String s) {
return s.replace("/", "_").replace("-", "_");
}
```
`sanitizeName` maps only `/` and `-` to `_`. Every other character — `;`, spaces, `(` `)` `{` `}` `[` `]`, `=`, `.`, `+`, `,`, digits — passes through verbatim into the generated Java.
The exploitable context is the Java identifier `col`. The same method also emits the raw column name into a Java string literal used by `tuple.getColumn("...")` (Expression.java:140/144). This string-literal context is not required for the exploit shown here, but it should still be escaped with `ValueExpression.escapeJavaString`, because it is another instance of raw user-controlled text emitted into generated Java source (including Java escape-sequence edge cases) and is therefore an additional source-generation hazard, not a closed surface. (`ValueExpression.escapeJavaString` is correctly applied to *value literals* such as `WHERE x = '...'`; function names are separately whitelisted by `FunctionExpressionFactory`. The unfixed gap is the column identifier.)
Why the column name is fully attacker-controlled
The StreamSQL grammar accepts any character except newline / CR / double-quote in a double-quoted identifier, and returns the raw inner content:
```
// org/yamcs/yarch/streamsql/StreamSql.jj:237 and :931
{String s1 = token.image; return s1.substring(1, s1.length() - 1);}
```
`ObjectName()` (used for column names in `CREATE TABLE` / `CREATE STREAM`) accepts this token, and neither `org.yamcs.yarch.ColumnDefinition` nor `TupleDefinition.addColumn` validates the characters of a column name (only a duplicate-name check). So `CREATE TABLE evil("" double, ...)` creates a column whose name is attacker-chosen text.
Why the bare-expression compiler is NOT exploitable, but the aggregate compiler IS
The general expression compiler `Expression#compile()` emits the column identifier in two contexts — a statement-context declaration *and* the `return col;` expression. A payload carrying executable statements (`;`-separated) makes the code after the `return` unreachable, which Janino rejects ("Statement is unreachable"); a payload without `;` cannot carry a side effect. That accidental barrier makes the bare-column path non-exploitable.
The aggregate path is different and exploitable: in `SumExpression`, both emissions of the column name live inside `newData(Tuple tuple)` — a `void`, statement-context method — and `getValue()` returns the accumulator `sum`, never the column. There is no expression-context `return col` and no unconditional `return` before the injected statements, so a `;`-separated, fully reachable payload compiles cleanly.
Reachability: `executeSql` reaches the aggregate compiler without the bare-path gate
`POST /api/archive/{instance}:executeSql` → `TableApi.executeSql` (gate `ctx.checkSystemPrivilege(SystemPrivilege.ControlArchiving)`, TableApi.java:398-399) → `ydb.execute(ydb.createStatement(statement))` → `SelectExpression.compile()`.
Crucially, when an aggregate's argument is a **plain c…
Related
Alias · CVE (1)
- CVE-2026-55511 Yamcs is a mission control framework. vulnerability
Weakness · CWE (1)
- CWE-94 Improper Control of Generation of Code weakness
References
- https://github.com/yamcs/yamcs/security/advisories/GHSA-3g44-3m7x-cgg2 web
- https://github.com/yamcs/yamcs/commit/8c1070b12c0a6c003903325cb2a1013347e2dbde web
- https://github.com/yamcs/yamcs/commit/b65a3d78178ba99a58b753feda6ecc3b5a694f13 web
- https://github.com/yamcs/yamcs package
- https://github.com/yamcs/yamcs/releases/tag/yamcs-5.12.8 web
- https://github.com/yamcs/yamcs/releases/tag/yamcs-5.13.2 web