# Stealing GitHub staff's access token via GitHub Actions

Self-hosted runner in actions/runner e2etest workflow ran untrusted PR jobs, letting a malicious PR steal a GitHub staff PAT with public_repo write access.

- Payout: Research
- Severity: critical
- Program: GitHub
- Write-up by: RyotaK
- Disclosed: 2023-04-22
- Write-up quality: 8/10
- Original: https://blog.ryotak.net/post/github-actions-staff-access-token-en/
- Library page: https://bugbountylibrary.com/r/blog%3Ablog-ryotak-net-post-github-actions-staff-access-token-en
- Tags: area:cloud domain:infra impact:integrity impact:internal-access kind:research platform:research sev:critical tech:github-actions technique:race-window technique:source-review vuln:secrets-leak

**Target:** GitHub Actions self-hosted runner / e2etest.yml workflow
**Root cause:** The e2etest workflow registers a self-hosted runner and runs untrusted pull_request-triggered jobs on it; the runner environment is not isolated between jobs, so a malicious PR executes code in the runner before the Remove Runner step.
**Steps:**
1. Observe e2etest.yml registers a self-hosted runner via `./config.sh --pat ${{secrets.PAT}}` and runs `./run.sh --once`
2. Note the async job-dispatch script sleeps 10s between polling registered runners, leaving a window after registration
3. Open a malicious PR against actions/runner whose workflow definition runs arbitrary commands
4. During the ~10s window the PR job executes on the freshly registered self-hosted runner
5. Subsequent `./config.sh remove --pat ${{secrets.PAT}}` runs in the compromised runner, exposing the PAT
**How found:** Reading the e2etest.yml workflow, noticed the async polling script with a 10s sleep creates a window between runner registration and job dispatch, and that the PAT is passed to a later step that runs on the now-compromised runner.
**Exploitation:** Attacker submits a PR to a public repo using a self-hosted runner; the PR's workflow runs on the shared runner, and the later Remove Runner step leaks secrets.PAT to attacker-controlled code, yielding a token tied to a GitHub staff account with public_repo scope over @actions/@github repos.
**Escalation:** Stolen PAT grants write to public repos of @actions and @github, enabling malicious commits to actions/checkout and actions/cache — a supply-chain compromise affecting downstream users.
**Insight:** In CI workflows, trace which secrets are passed to steps that execute after an untrusted job runs on a shared/self-hosted runner; a sleep/poll loop between registration and dispatch widens the race window for a malicious PR to land.
