OpenStack Mistral wave (4 GHSAs): the orchestrator ships its own exec primitive¶
Wave in one line: The orchestrator ships its own exec primitive, share grants outlive their owner's control, and the maintenance controller has no policy
Source: hourly offensive-security scan of GitHub Security Advisories, 2026-10-08 (wave published 2026-10-08T18:32Z, four advisories for OpenStack Mistral through 23.0.0).
Mistral is OpenStack's workflow/orchestration service — an API that, by design, runs actions on your behalf. All four legs are authorization/trust-boundary failures inside that design, and each generalizes to every workflow engine, job scheduler, CI orchestrator, or "automation" service you test: the engine's own feature set is the exploitation surface, and the sharing/membership plumbing is where tenancy quietly breaks.
Advisory table¶
| Leg | GHSA | CVE | Sev | Boundary |
|---|---|---|---|---|
std.ssh_proxied passes caller-supplied proxy_command to paramiko.ProxyCommand() — arbitrary local command executes on the executor before any SSH connection is attempted, under the executor service account | GHSA-gxqc-3r52-88h8 | CVE-2026-93858 | high | Any authenticated project member can run arbitrary commands on the executor host via the standard action-execution API. Default configuration is vulnerable — the action is permitted out of the box. The SSH connection never needs to succeed: the proxy command runs first. |
/v2/maintenance controller clears the request context and calls the maintenance service with zero policy enforcement — any valid Mistral token, any role | GHSA-858j-2m97-g3qm | CVE-2026-93860 | high | Read and set cluster-wide maintenance state. Setting PAUSED stops all workflow/execution processing across every tenant project = cross-tenant availability kill-switch from a single-member token. |
| Several v2 write paths resolve the target object with a query that can return another project's resource, then write to it; workbook name-collision "adopts" a foreign resource into the caller's project | GHSA-73hv-3hwx-q492 | CVE-2026-97147 | high | Rewrite/un-publish another project's public action definitions and environments; a project admin can create a workbook whose embedded ad-hoc action/workflow name collides with a resource of another project — the resource moves into the caller's project and the original owner's subsequent updates fail with server errors. |
Workflow membership API lets a project that accepted a share grant a further membership naming a third project, with project_id defaulted to the accepting project instead of the owner | GHSA-p254-mh9p-hpfm | CVE-2026-93861 | medium | The shadow membership is invisible and undeletable by the resource owner — only the accepting project can revoke. The third project can accept it and read/execute the owner's private workflow that nobody granted them. Persistent, owner-blind access path. |
Why this is an operator page¶
- Orchestrators ship built-in exec primitives — enumerate action inventories before hunting injection.
std.ssh_proxiedis not an injection bug in the classic sense: the documented feature (ProxyCommand, an SSH config option whose entire purpose is running a local program) is passed user input straight intoparamiko.ProxyCommand(). Audit rule for every workflow/automation product: pull the built-in action/module catalog (std.*here; Ansible modules, CI step libraries, scheduler plugins elsewhere) and look for actions whose legitimate semantics include local process launch, file write, or URL fetch. Fingerprint on an authorized cloud:POST /v2/actions/std.ssh_proxied/runreachability, or simply list the action namespace through the API. Because the command runs before the SSH connection is established, even a bogus gateway/target proves execution — the cleanest possible lab-bounded proof shape. - "Ops" controllers are the policy-free zone. The maintenance leg is the recurring pattern of a management controller that clears auth context "because it's maintenance": no policy check at all, gated only by holding any valid token. Sweep rule: on any multi-tenant API, enumerate
/maintenance,/admin,/health,/service,/statuscontroller families and test each with the lowest-tier token you can buy. A cross-tenantPAUSEDswitch is an availability finding with a governance angle (a single project member can halt every tenant's automation pipeline), and the read leg discloses cluster operational state. - Resolve-then-write without an ownership predicate is the supplied-scope-vs-owned-scope shape in its rawest form. The write-path leg is the same invariant as the October 8 DolphinScheduler legs on the Sept 18 ignored-scope page: the handler authorizes something, resolves a resource by a query that isn't scoped to the authorized tenant, then mutates whatever row came back. The collision-adoption variant is sharper: if creation of a name-colliding object can move a foreign resource into your scope, name-collision probing (
GETcandidate names, then workbook creation) doubles as cross-tenant resource enumeration and sabotage — the owner's update failures are a side effect worth knowing about before you touch a live cloud. - Sharing grants need owner-side lifecycle control or they become standing backdoors. The membership leg is the durable one for every product with a "share to another tenant/user" feature: the grant-creation path defaulted the scope field to the accepting project instead of the owning project, producing an access row the owner cannot see or delete, from which a third party can derive full read/execute on private content. Audit battery on any share/membership/invite API: (a) can an acceptor re-grant further memberships/shares it did not create? (b) which tenant ID does the new grant row carry — the owner's or the acceptor's? (c) as the resource owner, enumerate every access path to your own resource and try to revoke each one — any row that answers to a different project's revoke call is a persistence primitive an attacker who compromises the lowest-trust acceptor can plant.
- Deployment-shape gate: only deployments exposing the Mistral API are in scope (leg C note) — same class as the Splunk Patroni leg: internal OpenStack service ports (
8980for Mistral API, executor/conductor processes) appearing on a host is itself the finding before any exploit.
Replayable validation boundaries (lab/authorized-cloud only)¶
- Leg A: in a devstack/lab cloud, run
std.ssh_proxiedwithproxy_commandset to an inert marker (touch /tmp/mistral-cmd-markerequivalent, or a command that printsid) and confirm execution as themistralexecutor user from a plain project-member token. Never target a production cloud's executor host; proof is the marker +idoutput in the lab. - Leg B: with a lowest-tier token,
GET /v2/maintenance(state read), and only in an isolated lab, togglePAUSEDthen restoreOK— record that no policy error was returned. On a shared cloud, the read alone is the finding; do not pause production workflow processing. - Leg C: two disposable projects you own; publish a synthetic action in project B, then from project A run the write path with the resolved identifier and show project B's definition changed. Collision-adoption proof likewise between your own two projects only; never adopt/rewrite real tenant resources.
- Leg D: three disposable projects (owner / acceptor / third). Owner shares a private workflow containing only a synthetic marker to acceptor; acceptor creates the further membership naming the third project; third accepts, reads/executes the marker workflow; owner-side list/revoke calls are shown blind/failing. Clean up by revoking from the acceptor side.
- Evidence: token role used per call, raw request paths, DB-visible state transitions described in words (no live credentials, no real tenant workflow bodies).
Cross-references¶
- Supplied-scope-vs-owned-scope and per-verb ownership predicates: Sept 18 ignored-scope page (DolphinScheduler, Elasticsearch split-consumption, Zaqar per-transport tenancy legs).
- OpenStack Keystone-token scope family: the Zaqar WebSocket leg on the same page — same cloud, same "authenticated ≠ scoped" theme, different transport.
- Policy-free management controllers: Sept 21 auth-precedence page.
- Grant/re-share lifecycle drift genealogy: Oct 6 Payload CMS page (
useAPIKey, duplicate-op legs) and the Sept 21 delegation-scope legs.