Event March – April 2026 Club president and sole platform engineer
Operation: Black Meridian
A hacking competition for 100 players, built from scratch and shipped in 17 days
167 challenges, over 100 servers, 30 competing teams, 931 correct answers, one rented machine, one three-and-a-half hour window and no second chance.
What a CTF is
A capture-the-flag competition is a hacking contest. Teams are given deliberately broken software and have to break into it. Each successful break reveals a short secret string — the flag — which they submit for points. It is the standard way the security industry teaches and tests practical skill.
I was president of my university’s cybersecurity club, and our flagship event runs once a year. I built the platform the 2026 edition ran on.
The brief
Roughly a hundred players in teams of three, ranging from complete beginners to people who compete regularly. One rented server. A three-and-a-half hour window. No second chance.
Off-the-shelf competition software handles scoring and a list of challenges. It does not handle a field this uneven, it does not stop teams messaging each other the answers, and it does not make three people on a team matter more than one strong player with three logins. Those three problems drove the entire build.
Three problems, three solutions
Answers leak between teams
Flags get pasted into group chats. The standard fix is to give each team different answers, and the standard way to do that is to run a separate copy of every challenge for every team. Fifteen challenges across thirty teams is 450 servers. That does not fit on one machine, and it means every challenge has to be built to know which team is looking at it.
So I did it at the door instead. One copy of each challenge, with one answer baked in — and the traffic cop that sits in front of everything swaps that answer for the correct one for whichever team is asking, on the way out. Fifteen deliberately broken programs stayed completely unaware that teams exist, and were never rebuilt for anyone.
The detail that only shows up when you actually run it: the swap silently does nothing if the response is compressed. It took a live test to find that, and one line to fix.
The field was wildly uneven
Every stage of the competition splits into three difficulty tracks — guided, harder, hardest. The team captain picks once, and the platform then hides the other tracks entirely rather than just locking them, so nobody is served a wall of content they cannot use.
Points were assigned by hand rather than automatically adjusting to how many people solved each challenge. Automatic adjustment sounds fairer and is not: it quietly deflates whichever track has more players on it, until a challenge’s point value measures its popularity instead of its difficulty.
One strong player can carry three seats
Ten challenges were built so that all three teammates open the same link and each receives a different fragment of a hidden image, based on their position in the team. The three fragments only reveal the answer when combined. No single fragment shows anything at all.
One person cannot solve these. No amount of skill substitutes for a teammate being in the room. In the event data, every one of those ten challenges landed on exactly the same number of solves — the signature of a mechanism that depends on team composition rather than individual ability.
The system I would build again
Signing up is the only thing anyone had to do. Creating a team automatically triggered the platform to build that team its own private server, generate its passwords and access keys, add it to the traffic routing, and reload — with no human involved. Forty of those were created live during the event, with zero manual steps.
Underneath all of it is one small idea: every flag, password and access key in the entire competition is calculated from a single master secret plus the team’s number, rather than stored in a list somewhere. The same calculation is implemented in three different parts of the system, all reading the same one setting.
That means there is no list of secrets to keep in sync, no setup step that can quietly drift out of date, and rotating every credential for the entire event is a single value change.
What broke
The penalty system fired four times at once.
Teams accumulated a “heat” level for reckless behaviour, and high heat slowly drained points. The drain ran as a background timer started when the software loads — and the software runs as four separate copies for performance, so four independent timers ran, each applying its own penalty. The interval it ran at in production was also far shorter than the setting the code was written against, and the message shown to players described a slower rate than what was actually happening.
The result was 52 penalty entries written in a single second, across 13 teams. It was caught after one cycle and switched off live.
Two lessons. The obvious one: a background timer in a program that runs as four copies is four background timers, and I knew that in the abstract without applying it here.
The one I actually carry: anything that automatically takes something away deserves the same care as code that moves money. Bounded effects per interval, safe to run twice, and rehearsed specifically rather than assumed to work.
Results
| Competing teams | 30 | Challenges live | 167 |
| Registered accounts | 82 | Points available | 43,549 |
| Correct answers submitted | 931 | Servers at peak | 100+ |
| Total attempts | 1,506 | Time from empty repo to live | 17 days |
| Success rate | ~62% | Lines of code written | ~32,750 |
| Peak — teams active in one 30-minute window | 23 |
Before the event, a rehearsal simulating 150 players across 50 teams ran with zero failures, alongside a check of all 64 challenge servers and three rounds of independent testing with written reports and tracked fixes.
Afterwards, the entire system — code, database, uploads, website, configuration — was restored from backup onto a clean machine and verified working. That rehearsal found six real faults in the recovery process, each documented with its fix. A recovery plan that has actually been carried out is a completely different thing from one that has only been written down.
What I would do differently
- Automatically verify the answers on day three, not day fifteen. The script that checks every intended answer against what each challenge actually gives out is the most valuable tool in the project, and I wrote it under time pressure at the end instead of running it continuously from the start.
- Separate the networks, not just the routing. Isolation between the scoring system and the deliberately broken software was enforced by configuration. The next build separates them at the network level too, so safety does not depend on every route being set up correctly.
- Ship monitoring with the platform. How busy the system was, and how each part was holding up, could be reconstructed afterwards but not watched during. In a three-hour window that is the difference between fixing something live and explaining it later.
- Keep recording changes through the final week. The last stretch before an event is when the record of what changed matters most, and it is exactly when it is easiest to stop keeping one.
Challenges, answers, hints and solutions are not published — the platform is still in development for future events.