Challenge: Volatile Component · CTF: GPN CTF 2026 · Category: Miscellaneous · Difficulty: Medium
Summary
A GitHub Actions workflow leaks a FLAG secret but (1) masks it in logs, (2) redacts every GPNCTF{...} it finds on the filesystem, and (3) interpolates an untrusted issue/comment body straight into a run: shell block. The injection gives RCE; the flag survives only in the long-lived Runner.Worker process memory (the “volatile component”), which is read via sudo. Base64 of a secret is auto-masked by GitHub, so the flag is exfiltrated through the run logs as hex.
Solution
Step 1: Find the bug in the workflow
The instancer hands you a private repo containing a single workflow:
| |
Three observations:
- Step 3 drops
github.event.issue.bodyraw into a shell command → classic script injection / RCE. - Step 2 proves
sudois passwordless, but it only rewrites files. - The
Runner.Workerprocess must keep the secret value resident in memory to keep masking output — so the unredacted flag lives in/proc/<pid>/memeven after disk redaction.
Step 2: Bypass log masking with hex, not base64
The flag is masked (***) in logs. GitHub also auto-registers the base64 form of every secret for masking, so base64(flag) prints as *** too. Hex encoding is not auto-registered and passes straight through.
Step 3: Inject, dump memory, exfiltrate as hex
Open one issue whose body breaks out of the echo and runs a sudo memory scanner. The clean flag is recovered from process memory and printed as hex, then read back from the run logs (pull/read access is granted to the player). See solve.py.
Test the payload on a personal fork (with a dummy
FLAGsecret) first — the live instance burns limited GitHub Action minutes and the organizers warn against testing on it.
Flag
| |