Executive Summary
CVE-2026-32613 is a critical remote code execution (RCE) vulnerability in Spinnaker's Echo microservice, disclosed on April 20, 2026, carrying a CVSS score of 9.9. The flaw stems from unrestricted Spring Expression Language (SPeL) evaluation when processing expected artifact information — allowing any user with pipeline access to invoke arbitrary JVM classes, execute OS commands, and read or modify files on the underlying host. Organizations running Spinnaker for CI/CD must patch or disable Echo immediately.
1. What Is This Vulnerability?
Spinnaker is Netflix's open-source, multi-cloud continuous delivery platform, widely used in enterprise DevOps pipelines to orchestrate deployments across AWS, GCP, Azure, and Kubernetes. Its Echo microservice handles event notifications and artifact matching, using Spring Expression Language (SpEL) to evaluate expressions tied to expected artifacts in pipeline definitions.
The vulnerability exists because Echo evaluates user-supplied SPeL expressions against the full JVM context without restricting which classes are accessible. Unlike Spinnaker's Orca service — which constrains SpEL evaluation to a safe allowlist of trusted classes — Echo performed no such sandboxing. This means a malicious actor who can define or influence pipeline artifact expectations can craft a SpEL expression that calls arbitrary Java classes, ultimately executing OS-level commands on the server running Echo.
Attack Vector
An authenticated user (or a compromised service account) with access to Spinnaker's UI or API can inject a malicious SpEL expression into an expected artifact's configuration. When the Echo service evaluates this expression, the attacker gains code execution in the context of the Echo process.
Example malicious SpEL payload (for illustrative/defensive purposes):
// Exploit: calling Runtime.exec() through SpEL
#{T(java.lang.Runtime).getRuntime().exec('id')}
// More dangerous variant reading files
#{new java.util.Scanner(T(java.lang.Runtime).getRuntime()
.exec(new String[]{'/bin/bash','-c','cat /etc/passwd'})
.getInputStream()).useDelimiter('\\A').next()}
Because Echo runs inside the Spinnaker deployment (often with broad cloud credentials or Kubernetes service account permissions), successful exploitation can pivot to cloud infrastructure, secrets stores (e.g., Vault, AWS Secrets Manager), or internal services reachable from the cluster network.
Real-World Impact
As of disclosure, no confirmed public breach has been attributed to CVE-2026-32613. However, the severity is high because:
- Spinnaker instances are frequently deployed with elevated cloud IAM permissions to orchestrate deployments.
- Many organizations expose Spinnaker's API internally with broad service account access, widening the blast radius.
- SpEL injection vulnerabilities in Spring-based systems have a documented exploitation history (e.g., CVE-2022-22963, Spring4Shell CVE-2022-22965), and working proof-of-concept exploitation techniques are well-understood by attackers.
2. Who Is Affected?
Any Spinnaker deployment running the Echo service on an unpatched version is vulnerable:
| Spinnaker Release Branch | Vulnerable Versions | Patched Version |
|---|---|---|
| 2026.1.x | < 2026.1.0 | 2026.1.0 |
| 2026.0.x | < 2026.0.1 | 2026.0.1 |
| 2025.4.x | < 2025.4.2 | 2025.4.2 |
| 2025.3.x | < 2025.3.2 | 2025.3.2 |
| 2025.2.x and earlier | All versions (EOL) | No patch — upgrade required |
Severity modifiers to consider:
- Higher risk if Echo is accessible to untrusted users, exposed via an API gateway, or if Spinnaker service accounts have broad cloud permissions.
- Lower risk if Echo is only reachable by a small, trusted set of internal operators and network access is tightly restricted.
3. How to Detect It (Testing)
Manual Testing Steps
-
Identify your Echo version: Query the Spinnaker Gate API or check your Helm chart / Halconfig to determine which version of Echo is deployed.
kubectl get pods -n spinnaker -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.spec.containers[0].image}{"\n"}{end}' | grep echo -
Inspect artifact configuration for user-controlled expressions: Review pipeline definitions in Spinnaker's UI or via API for expected artifact fields that accept text input. Any free-form field that feeds into SpEL evaluation is a potential injection point.
# Export all pipelines and grep for SpEL expression markers curl -s http://<spinnaker-gate>:8084/applications | \ jq '.[].name' -r | while read app; do curl -s "http://<spinnaker-gate>:8084/applications/$app/pipelines" done | grep -o '#{\([^}]*\)}' | sort -u -
Attempt a safe canary SpEL expression (authorized testing only): In a non-production environment with permission, inject a benign expression like
#{1 + 1}into an expected artifact name field and observe whether Echo evaluates it and returns2. Successful evaluation confirms the unsandboxed execution context.
Automated Scanning
Tool: Nuclei with a custom template
Command:
nuclei -t spinnaker-spel-rce.yaml -u http://<spinnaker-gate>:8084 \
-H "Authorization: Bearer <token>"
Custom Nuclei template (spinnaker-spel-rce.yaml):
id: CVE-2026-32613-spinnaker-spel
info:
name: Spinnaker Echo SpEL RCE
severity: critical
tags: spinnaker,spel,rce,cve-2026-32613
requests:
- method: POST
path:
- "{{BaseURL}}/pipelines"
headers:
Content-Type: application/json
body: |
{"expectedArtifacts": [{"matchArtifact": {"name": "#{T(java.lang.System).getProperty('os.name')}"}}]}
matchers:
- type: word
words:
- "Linux"
- "Windows"
condition: or
Expected output indicating vulnerability: Response includes the evaluated OS name string rather than the literal expression.
Tool: Semgrep (for static code review of custom Spinnaker extensions)
semgrep --config=p/spring \
--pattern 'new SpelExpressionParser().parseExpression($EXPR).getValue($CTX)' \
./your-spinnaker-extensions/
Code Review Checklist
For teams running custom Spinnaker extensions or evaluating SpEL internally:
- Verify all SpEL evaluations use
SimpleEvaluationContext(notStandardEvaluationContext) unless full JVM access is explicitly required - Confirm evaluation context is restricted to an allowlist of safe types via
TypeLocatororSimpleEvaluationContext.forReadOnlyDataBinding() - Check that user-supplied strings are never passed directly to
SpelExpressionParser.parseExpression()without validation - Ensure Echo microservice version is ≥ the patched version for your release branch
- Review Spinnaker network policies so Echo is not directly reachable from untrusted network segments
4. How to Fix It (Mitigation)
Step-by-Step Remediation
-
Identify your current Spinnaker version using the Halyard CLI or your Helm values:
hal version current # or helm list -n spinnaker -
Update to a patched version. If using Halyard:
hal config version edit --version 2026.1.0 hal deploy applyIf using the Spinnaker Helm chart, update your
values.yaml:spinnakerVersion: "2026.1.0"Then apply:
helm upgrade spinnaker spinnaker/spinnaker \ -f values.yaml -n spinnaker -
If immediate patching is not possible — disable Echo as a temporary workaround:
# Scale Echo to zero replicas kubectl scale deployment spin-echo --replicas=0 -n spinnakerNote: Disabling Echo will stop pipeline event notifications and some trigger functionality. Assess operational impact before doing this in production.
-
Validate the rollout (see Section 5) before restoring full traffic.
Code Fix Example
The upstream patch restricts SpEL evaluation in Echo to use SimpleEvaluationContext, preventing arbitrary class resolution. The conceptual change mirrors this pattern:
Before (vulnerable):
ExpressionParser parser = new SpelExpressionParser();
StandardEvaluationContext ctx = new StandardEvaluationContext(artifactContext);
// No class restriction — full JVM access available
Object result = parser.parseExpression(userExpression).getValue(ctx);
After (patched):
ExpressionParser parser = new SpelExpressionParser();
// Restricted context: no arbitrary type resolution, no method invocation
EvaluationContext ctx = SimpleEvaluationContext
.forReadOnlyDataBinding()
.withInstanceMethods()
.build();
Object result = parser.parseExpression(userExpression).getValue(ctx, artifactContext);
Configuration Hardening
Even after patching, apply these hardening measures:
-
Network policy: Restrict access to the Echo service so it is only reachable from trusted internal services, not from user-facing network segments.
# Kubernetes NetworkPolicy example apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: restrict-echo-access namespace: spinnaker spec: podSelector: matchLabels: app: spin-echo ingress: - from: - podSelector: matchLabels: app: spin-gate - podSelector: matchLabels: app: spin-orca -
Least-privilege service accounts: Audit and trim cloud IAM permissions granted to Spinnaker's service accounts to the minimum required for deployments.
-
Authentication & authorization: Ensure Spinnaker's Gate API requires authentication for all endpoints; disable anonymous access.
5. How to Test the Fix (Validation)
Regression Test Scenarios
- Scenario A: Confirm patched Echo evaluates legitimate artifact match expressions correctly and pipeline triggers still function.
- Scenario B: Confirm the original SpEL injection payload no longer executes arbitrary code (expression is rejected or returned literally rather than evaluated).
- Scenario C: Confirm pipeline notifications (Slack, email, webhooks) continue to fire after patching or re-enabling Echo.
Security Test Cases
Test Case 1: Verify the vulnerability no longer exists
- Precondition: Echo is updated to a patched version and deployed.
- Steps: In a non-production Spinnaker environment, submit a pipeline with an expected artifact whose name contains
#{T(java.lang.Runtime).getRuntime().exec('id')}. - Expected Result: Echo either throws an evaluation error or returns the literal string — it does NOT execute the
idcommand or return its output.
Test Case 2: Verify legitimate SpEL still works as intended
- Precondition: Patched Echo is deployed.
- Steps: Configure an expected artifact that uses a permitted SpEL expression for artifact matching (e.g., matching on
artifactAccountproperty). - Expected Result: Pipeline proceeds normally; the artifact is matched correctly.
Test Case 3: Verify network-level access controls
- Precondition: NetworkPolicy applied restricting Echo access.
- Steps: Attempt to reach the Echo service port (8089) from a pod outside the allowed set.
- Expected Result: Connection is refused or timed out.
Automated Tests
import requests
import pytest
GATE_URL = "http://spinnaker-gate:8084"
AUTH_TOKEN = "your-test-token"
SPEL_PAYLOAD = "#{T(java.lang.Runtime).getRuntime().exec('id')}"
def test_spel_injection_blocked():
"""CVE-2026-32613: SpEL expressions must not result in command execution."""
resp = requests.post(
f"{GATE_URL}/pipelines/test-app/test-pipeline",
json={
"expectedArtifacts": [{
"matchArtifact": {"name": SPEL_PAYLOAD}
}]
},
headers={"Authorization": f"Bearer {AUTH_TOKEN}"},
timeout=10
)
# The response must not contain output typical of command execution
response_text = resp.text
assert "uid=" not in response_text, "SpEL command execution succeeded — PATCH NOT APPLIED"
assert "root" not in response_text.lower() or "matchArtifact" in response_text
6. Prevention & Hardening
Best Practices
-
Always sandbox user-controlled expression evaluation. Any time user input flows into a SpEL (or similar) expression evaluator, use the most restrictive evaluation context available. Default to
SimpleEvaluationContext; only escalate toStandardEvaluationContextwhen absolutely necessary and with careful review. -
Apply Spinnaker updates promptly. Subscribe to the Spinnaker security advisories on GitHub and integrate Spinnaker version updates into your standard patch management cycle.
-
Rotate credentials after any suspected exploitation. Because Echo runs with cloud credentials, treat any confirmed exploitation as a full credential compromise event: rotate cloud IAM keys, invalidate tokens, and audit cloud activity logs for the Echo service's identity.
-
Implement regular dependency auditing. Use tools like
trivyorgrypeto scan Spinnaker container images for known vulnerabilities as part of CI pipelines:trivy image us-docker.pkg.dev/spinnaker-community/docker/echo:2026.1.0
Monitoring & Detection
Set up alerting for signs of exploitation or reconnaissance targeting Echo:
-
Audit SpEL expression content: Log all artifact match expressions processed by Echo and alert on patterns matching SpEL injection signatures (
T(,getRuntime,exec():# Example: grep Echo logs for injection patterns kubectl logs -n spinnaker -l app=spin-echo --since=1h | \ grep -E 'T\(java\.lang|getRuntime|\.exec\(' -
Cloud activity anomaly detection: Alert on unexpected API calls from the Spinnaker service account — especially ListRoles, CreateRole, AssumeRole, or any data exfiltration-related calls.
-
Kubernetes audit logs: Monitor for unusual subprocess activity spawned from the
spin-echocontainer using a runtime security tool like Falco:# Falco rule: detect shell spawned from Echo - rule: Shell Spawned from Spinnaker Echo desc: A shell was spawned from the spin-echo container condition: > spawned_process and container.name = "echo" and proc.name in (shell_binaries) output: "Shell spawned from Spinnaker Echo (user=%user.name cmd=%proc.cmdline)" priority: CRITICAL -
Network egress monitoring: Alert on unexpected outbound connections from the Echo pod, which may indicate a reverse shell or data exfiltration attempt.
References
- CVE Record: CVE-2026-32613 on TheHackerWire
- Detailed Write-up: Spinnaker Echo Critical RCE via SPeL – TheHackerWire
- Patch Info: Upgrade to Spinnaker 2026.1.0, 2026.0.1, 2025.4.2, or 2025.3.2 via Spinnaker releases on GitHub
- SpEL Injection Techniques: Leveraging SpEL Injection to get RCE
- Spring Security Guidance: Spring Expression Language (SpEL) – Spring Docs
- CISA KEV Catalog: CISA Known Exploited Vulnerabilities
- Falco Runtime Security: https://falco.org