kalachakra.world is normally a quiet place: a public lore browser for the cyberpunk world I have created, and a community site that includes a fan gallery, messaging, and world-related merch (most of which is in-world fiction). During specific promotional windows, however, it becomes something else — a capture-the-flag (CTF) event with a bounty board, leaderboard, and a set of deliberately vulnerable challenges planted in the site. When the event ends, it turns back into a lore site. I did this because I thought it would be really cool to be able to “hack” the site that features hackers as heroes.
Something that makes it even cooler is that between events, the CTF isn’t hidden, and it isn’t switched off but lying dormant for someone to probe. It genuinely isn’t there. Anyone poking at the site between events finds no bounty board, no /api/ctf endpoints, and — most importantly — no vulnerable code sitting in the request path.
I wanted to briefly outline how that works.
Hide and Seek
When I toggle a CTF promotion, two things come online: the CTF pages and the CTF challenges.
The pages appear and disappear
The bounty board, the leaderboard, and the /api/ctf/* endpoints are not part of the standard site. They get registered with the running app only when a promotion activates, and are removed when it ends.
A request to /api/ctf/challenges, while kalachakra.world isn’t hosting a CTF event, doesn’t reach a handler that replies “no event right now” — it returns a 404 because that route doesn’t exist. The frontend follows the same approach: it only registers the /bounties and /leaderboard routes while an event is live, so the links aren’t in the menu and those URLs go nowhere.
The challenges are swapped in at “injection points”
The vulnerabilities don’t live in the normal code as if event_active: do_the_insecure_thing. Instead, the app defines named injection points — specific spots on real endpoints where the handler can be swapped out. Each one ships with a secure default.
register_injection_point(
name="avatar_serve_path",
default=safe_path_resolver, # ignores anything sneaky
)
In standard mode the secure default runs. There’s no branch and no “CTF mode” check in the request path — the safe handler is simply what’s wired in.
When a promotion activates and a challenge is drawn, its module swaps a vulnerable handler into its injection point:
inject(
name="avatar_serve_path",
handler=path_traversal_resolver, # the bug, for this event only
)
Now that endpoint behaves insecurely on purpose, and there’s a flag to find. When the event ends, every injection point is restored to its default. The vulnerable handlers still exist in the codebase, but they aren’t wired into anything, and so there is no surface area to exploit.
The lifecycle
The flow of a CTF promotion is as follows:
- Draw a set of challenges from the pool (by default seven: a couple each of the easy, medium, and hard tiers, plus one decoder puzzle).
- Generate a flag for each and plant any decoy files the challenge needs.
- Swap the vulnerable handlers into their injection points.
- Mount the CTF pages and API routes.
- Flip the
promotion_activeflag that the frontend reads.
Deactivating runs the same list in reverse: archive the leaderboard, restore the injection points, remove the decoys and the routes, and clear the flag.
One wrinkle: the swaps live in memory, so a server restart mid-event would lose them. On startup the app checks whether an event is marked active and, if so, replays the activation — re-injecting the handlers, re-mounting the routes, reloading the flags it had generated. From the outside, a restart is invisible.
Flags
A flag is just a random string. Submissions are checked against a stored hash rather than the raw value, so the server can confirm a guess without keeping the answer sitting in plain sight. Solving a challenge credits an in-world currency you can spend on raffles and merch.
That’s the whole mechanism: named seams with safe defaults, vulnerable handlers swapped in only during an event, and the CTF’s pages mounted and unmounted around it. In standard mode, none of it is present.
Join the Beta
I wanted to lay this out, because the functionality is currently in beta testing and it is my hope to attract that Venn diagram of folks who are cyberpunk fans and are enthusiastic about cybersecurity. If you are someone who exists within that set, I would genuinely appreciate any and all feedback you have. Just visit kalachakra.world and join the community! It is free—only requiring your email.
I proudly honor eff.org’s DO NOT TRACK guidelines, and you’ll find my site policy is pro-privacy (kalachakra.world/privacy). To reach out to me, you can use my email (resonate@bjbell.com), the contact form here on bjbell.com, or the PM system within kalachakra.world (PM The Actual BJ Bell).