infrafreehard

Old food

gpnctf24

Task: GitHub Actions CI/CD security challenge with a vulnerable pull_request_target ci.yml (checks out attacker PR merge-ref) and a deleted flag.yml that references secrets.FLAG, all in a private per-player repo. Solution: achieve privileged RCE via the PR merge-ref checkout, recover the contents:write GITHUB_TOKEN from actions/checkout-persisted .git/config, roll the default branch back via the REST git refs API to resurrect the deleted flag.yml (bypassing the workflow-scope push protection), then open a triggering PR to run it and exfiltrate the double-base64 secret into the public run logs.

$ ls tags/ techniques/
pull_request_target_privileged_rcepr_merge_ref_checkoutgithub_token_recovery_from_git_configworkflow_resurrection_via_refs_apiworkflow_scope_push_bypasssecret_exfil_to_public_logsdouble_base64_decode

$ cat /etc/rate-limit

Rate limit reached (20 reads/hour per IP). Showing preview only — full content returns at the next hour roll-over.

Old food — GPNCTF24 (GPN24 / KITCTF)

Description

Old food, gold food, mold food, bold food, bad f00d, whatever. ncat --ssl steamed-filet-under-sliced-harissa-uz0h.gpn24.ctf.kitctf.de 443

English summary: Connecting to the service provisions a private per-player GitHub repository (a Node.js "Fresh Bite" recipe app) and adds the player as a read-only collaborator. A repository Actions secret named FLAG holds the flag. The repo's CI workflow uses the privileged pull_request_target trigger and checks out the attacker's PR code, but ci.yml never references secrets.FLAG. A second workflow, flag.yml, does reference the flag — but it was deleted from main and only survives in git history. The goal ("Old food" = stale/old workflow that can be resurrected) is to bring the deleted workflow back to life and make it leak the secret. The author, intrigus-lgtm, is a known GitHub Actions security researcher, confirming the theme.

Analysis

Provisioning mechanism

The ncat --ssl ... 443 endpoint prompts for a GitHub username. A backend (server.pynode index.js, using an authenticated GitHub PAT) then:

  1. Resolves the GitHub user's actor_id via the GitHub API.
  2. Creates a private repo <actor_id>_<username>_old-food-challenge in org GPNCTF24-2.
  3. Mirror-pushes a template carrying many branches (main, feature/*, fix/*, ...) and the full git history.
  4. Waits for ci.yml to appear, then adds the player as a pull (read-only) collaborator.
  5. Adds a repository Actions secret FLAG — the real flag.

Reconnaissance trick: probing the prompt with a throwaway username (e.g. octocat) leaks the org name GPNCTF24-2 and the repo-naming scheme through the node error output, even before owning an instance. The service warns not to test against the live instance (limited Actions minutes) — develop the exploit on a personal fork, then fire once at the real instance.

...

$ grep --similar

Similar writeups