MCTF 5.0 Blog

MCTF Behind the Scenes: The Cheaters We Caught and How Dynamic Flags Saved Us

How we caught teams sharing flags, sat on the evidence for twelve hours, and had one of the most awkward conversations of our lives an hour before the competition ended

Anti-cheat System and Dynamic Flags in MCTF 5.0 Blog Post

How Dynamic Flags Work

Before the story, a quick detour. Just one thing. Everything that follows depends on it.

In MCTF, every team gets their own flag per challenge. Not a shared flag, theirs. When a team opens a challenge for the first time, our instancer generates a unique flag: the base flag plus 16 random hex characters, specific to that team, stored once and never regenerated. When someone submits a flag, we don’t just see that it’s correct. We see who it was generated for.

If Team A’s flag shows up in Team B’s submission history, there’s exactly one explanation for that. You’ll see why this matters in a minute.


The Discovery

It was around 8pm of the second day. The third wave of challenges had just gone live and the room had that specific energy: heads down, keyboards going, the scoreboard updating every few seconds. We were in that brief window where everything feels like it’s working and you can finally exhale.

We were doing one last check of the instancer dashboard after the wave was launched. The kind of check where you’re mostly just enjoying seeing green numbers. Then we scrolled past a section we usually ignore.

The anti-cheat tab had something in it.

Anti-cheat dashboard showing a suspicious submission on the Matryoshka challenge at timestamp 19:33:35
Same flag. Two different teams.

Same challenge. Same flag. Two different teams.

Matryoshka is a misc challenge. The flag had been generated at 2:10am when team A first opened it. They solved it that afternoon at 3:11pm. Then at 7:33pm, about four hours later, a completely different team submitted that exact flag. Not a lucky collision. The same hex sequence, byte for byte, generated for a specific team seventeen hours earlier.

We pulled the raw CTFd records just to be sure.

CTFd record showing the flag content, username, team name and creation timestamp
Flag generated at 2:10am, solved at 3:11pm. Submitted by another team at 7:33pm.

flag_content, team_name, username. All pointing to the team that originally solved it. Submitted by someone else.

Someone had shared their flag. That was the only explanation that fit.


Twelve Hours

It was Friday night. The CTF had started Thursday, so this was already the second night. Around fifty people still in the room, teams at their tables, the usual late-night energy of a hacking event. You could walk up to anyone in thirty seconds.

We had two reasons not to say anything yet.

The first was practical. If we confronted them right then, the outcome was disqualification, no way around it. Disqualification in this case meant they’d have to leave the hacking space. That’s not a reasonable thing to ask of anyone. It was late at night, they had no way to leave safely at that hour, and we weren’t going to put them in that position over something we could just as easily handle in the morning.

The second was strategic. We didn’t want to reveal that the anti-cheat system had fired. The whole point of having it is that people don’t know exactly when it watches or when it acts. If we walked up to their table right then, every team in the room would know the system catches flag sharing in real time and that we respond immediately. That’s useful information for anyone else who might be thinking about it. We wanted to keep it quiet, keep it running, and deal with it when the competition was over.

So we made a call that felt uncomfortable in the moment: sit on it. Let the competition finish. Handle it properly in the morning.

The CTF closed at 9am, we waited twelve hours.


8am. One Hour Before the End.

With an hour left on the clock, we walked across the hacking space.

There’s a specific feeling when you’re about to have a conversation you’ve been carrying for twelve hours. Your brain has had time to imagine every version of it. None of them are good.

We found team A at their table. And a member of team B was sitting right next to them.

That answered the first question before we’d asked it.

We kept the opening simple.

“Do you know each other? Are you friends?”

The answer came immediately.

“Yes. We are friends, we study together.”

With both of them right there, we laid out what we had. At 7:33pm the previous evening, a flag generated for one team had been submitted by the other. The flag for Matryoshka, generated at 2:10am and solved at 3:11pm by team A, submitted four hours later by team B. We had the CTFd records: flag content, team name, username, all pointing to one team, submitted by the other.

Then we stopped talking and gave them the floor.

The table went quiet. You could see them working through it, teammates glancing at each other, and within seconds team A remembered who solved that challenge, probably the one who shared the flag. They turned to him and asked directly: why would you do that?

He didn’t deflect. Embarrassed, but honest: he’d shared the flag with his friend from team B. Probably didn’t think hard about what it meant in a competition.

Just a quick answer to “what’s the flag for that one,” the kind of help that feels harmless between friends until you’re sitting across from the organizers at 8am realizing it wasn’t.

We kept it short. Clear about what happened, no lecture, no spectacle.

These competitions exist to show what you can do. The person who shared the flag probably didn’t think of it as cheating. It was.
The people who lost here were their teammates. They spent two nights genuinely solving challenges, only for one teammate to break a rule and get the entire team disqualified.


What Dynamic Flags Actually Buy You

Here’s what our anti-cheat system didn’t do: it didn’t monitor network traffic, plant honeypots, or watch anyone’s screen. We didn’t catch anyone in the act. We ran a routine check, and the data told us exactly what happened.

That’s the whole point. Dynamic flags don’t prevent cheating. If someone wants to share a flag, they’ll share a flag. What they do is make every shared flag a signed confession. You don’t need to catch people. You just need a system where the evidence is the evidence, and the conversation starts with “we know” rather than “we suspect.”

One closes the conversation. The other starts an argument you can’t win.

If you’re running a CTF, implement dynamic flags. Not because you expect your players to cheat. Most won’t. But because when it does happen, you want to be able to look someone in the eye and tell them the truth without hedging. That’s worth the engineering.

We walked back across the room. The clock kept ticking. The competition finished an hour later. And the anti-cheat dashboard? We closed that tab and didn’t open it again until the event was over. Some things are better dealt with one at a time.