The Build That Packed Its Own Key: Recovering GITHUB_TOKEN From an Actions Artifact

Cloud Security Level 3/4 ~5 min November 9, 2026

The challenge

Kelpforge's CI shared its whole checkout. Find the auth header git kept, decode it. Bring back the token the build job ran with.

What you'll learn

  • Explain where actions/checkout stores the job's token and why
  • Find hidden files and folders inside a CI artifact
  • Decode an HTTP basic credential from base64
  • Tell a GitHub Actions token (ghs_) from a personal token (ghp_)
  • Harden a workflow: persist-credentials, artifact paths, permissions

Skills tested

CI/CD securityLinux file searchCredential hunting

Prerequisites

  • Basic shell commands (ls, cat, grep)
  • What a CI pipeline is

How it works

Every GitHub Actions job gets a short-lived GITHUB_TOKEN (prefix ghs_), scoped to its repository with the permissions the workflow grants. actions/checkout writes it into the checkout's .git/config by default (persist-credentials: true), as an http.https://github.com/.extraheader carrying AUTHORIZATION: basic and the base64 of x-access-token:<token>, so that later git push steps just work.

Base64 is an encoding, not encryption: anyone who reads the file gets the token. In 2024 Unit 42 found public repositories whose workflows uploaded the whole working directory as an artifact (they called it ArtiPACKED). Artifacts of public repositories can be downloaded by any signed-in user, and since actions/upload-artifact@v4 they are available while the job is still running, while the token is still valid.

Common mistakes

  • Using plain ls. .git, .github and .npmrc are dotfiles: use ls -a, find or grep -r.
  • Answering the ghp_ token. The commented line in .npmrc is an old personal access token (ghp_). The job ran with a ghs_ token.
  • Answering the base64 value. It is the encoded user:password pair; decode it and keep only the part after x-access-token:.
  • Taking ${{ secrets.GITHUB_TOKEN }} literally. The workflow only names the secret; its value is in the checkout's config.
  • Waiting for git. This box has no git installed: cat .git/config reads the same thing git config --list would.

How to defend against it

  • Set persist-credentials: false on actions/checkout, and pass a token explicitly to the one step that pushes.
  • Upload only the build output (path: dist), never ., and leave include-hidden-files at its default false.
  • Declare the minimum permissions: for the job (contents: read unless it must push).
  • Scan workflows for these patterns (zizmor flags ArtiPACKED) and rotate any long-lived secret that ever landed in an artifact.

Full solution

Pro and Max members unlock the complete step-by-step walkthrough.

Go Pro

Community stats

171 completions
89% success rate
M2F14M3 First blood

Related Daily Hacks

38,000+ Hackers Real labs Free
Start Hacking Free or solve today's hack, no account needed