Writeups, solve scripts, and tooling for the FlagYard 160+ challenge training-lab pool - spanning Crypto, Reverse Engineering, Forensics, PWN, Web, Hardware, and a few bundled CTF events (CyberNights 2024/2025, SAFCSP booth @ BlackHat MEA).
See PROGRESS.md for the full per-challenge table (status, points, flag).
challenges/<Category>/NN-<slug>/
metadata.json # challenge id, points, difficulty, solved status
solution.md # analysis + flag
files/ # challenge-provided source/binaries (large raw artifacts are gitignored)
scripts/ # one-off solve scripts, where a challenge needed one
- fy_api.py - FlagYard API client (auth, challenge detail, file download, flag submission)
- scrape.py - builds/updates the
challenges/tree from the live API - solve.py - instance lifecycle + flag submission + progress bookkeeping
- vps.py - SSH helper for offloading heavy crypto/forensics tooling to a remote box
- tools/ - reusable helpers (PE/ELF disassembly, Coppersmith/LLL, remote triage)
fy_api.py reads a Keycloak bearer JWT from a scratchpad token.txt (see the constants
at the top of that file). This token lives in a session-specific scratchpad directory that
gets cleaned up between sessions, so it needs to be refreshed periodically:
- Log into flagyard.com in a browser.
- Open DevTools → Application → Local Storage →
flagyard.com, and find theauthkey (a JSON blob with"accessToken": "...") - or grab theAuthorization: Bearer ...header off any authenticated request in the Network tab. - Save just the raw JWT string to the path
fy_api.TOKEN_FILEpoints at (or set theFY_TOKEN_FILE/FY_SCRATCHenv vars to point at wherever you saved it).
Cookies alone (flagyard.com_cookies.json-style exports) are not sufficient - the
frontend is a Keycloak SPA, so the actual access token lives in localStorage, not
cookies. If localStorage.auth.isLoggedIn is false, you're logged out and need to log
in again before exporting.
Raw challenge artifacts over ~5MB (memory dumps, disk images, pcaps) are excluded via
.gitignore - the writeup in each solution.md plus the small solve scripts capture what
matters without bloating the repo.
Training-lab challenge flags (everything except the CyberNights 2024/2025 live-CTF
folders) appear to be static/shared across all accounts rather than per-user - confirmed
by several external writeups showing byte-for-byte identical flags to ones already solved
in this repo (SM, FindTheLeak, CU29, nooter). That means a verified external writeup can
be cross-checked against our own files/binaries and, once independently confirmed (see
Competitive Reversing/04-locker for a worked example), applied here - but a flag should
never be copied in blind. metadata.json.flagCandidate marks a flag that's been recovered
this way but not yet live-submitted; solve.py mark promotes it once FlagYard accepts it.