Key takeaways
- Static flags cannot tell you who leaked them. Per-team dynamic flags turn every leak into evidence you can attribute.
- A common way to build them is
HMAC(secret, challenge_id:team_id): deterministic, no flag database, unforgeable without the secret.- A team submitting another team's flag is the strongest evidence you will get. IP and browser signals only back it up.
- Rate limits, honeypot flags and a published anti-cheat policy with an appeal path cover what dynamic flags can't.
Running an event? CTF7 offers dynamic flags and anti-cheat monitoring on the Standard and Premium plans. See pricing or host my event.
The flag-sharing problem
Every organizer of a public CTF knows the pattern. A hard challenge sits at zero solves for six hours, then three teams solve it within four minutes of each other. Somewhere a flag went out through a Discord DM, a WhatsApp group or a screenshot passed around a hostel.
Cheating in CTFs usually takes one of four forms:
- Flag leaks. A player passes a flag to a friend on another team, often without seeing it as cheating.
- Collusion between teams. Two "separate" teams split the challenge board and swap flags, doubling their output.
- Multi-accounting. One strong player registers several accounts or teams, either to brute-force answers or to push a second team up the rankings.
- Mid-event writeups. Someone publishes a solution while the competition is still running, and anyone who finds it scores those points.
All four hurt honest players. At a college fest the prize goes to the wrong team. At a hiring CTF it's worse: you shortlist the candidate with the best group chat, not the best skills.
Why static flags leak so easily
A static flag is the same string for everyone, such as CTF7{This_is_an_example_flag}. It is easy to write, check and ship inside a downloadable file, but it carries no information about who found it. Once it leaks, a copied submission looks exactly like a genuine solve.
A dynamic flag is generated separately for each team, or for each player in solo events. Team A's flag for web-200 is different from Team B's. If Team B submits Team A's flag, you don't need to guess what happened, because the flag itself shows it came from Team A. For the short definition and a side-by-side comparison table, see the dynamic flag glossary entry; the flag format entry and the rest of the CTF glossary cover related terms.
Generating per-team flags with HMAC
Instead of storing a table of random flags, derive each flag from an HMAC of the challenge and team IDs, keyed with a server-side secret. The output is deterministic, so the scoring server can recompute any team's flag on demand, and without the secret nobody can predict or forge one.
import hmac, hashlib
SECRET = b"rotate-me-per-event" # server-side only, never inside a challenge image
def team_flag(challenge_id: str, team_id: str) -> str:
msg = f"{challenge_id}:{team_id}".encode()
digest = hmac.new(SECRET, msg, hashlib.sha256).hexdigest()[:24]
return f"CTF7{{{digest}}}"
def check(challenge_id, team_id, submitted, all_teams):
if hmac.compare_digest(submitted, team_flag(challenge_id, team_id)):
return "correct"
# Wrong flag: does it belong to another team?
owners = {team_flag(challenge_id, t): t for t in all_teams}
if submitted in owners:
return f"FLAG SHARING: flag belongs to team {owners[submitted]}"
return "incorrect"
teams = ["t-101", "t-102", "t-103"]
print(check("web-200", "t-101", team_flag("web-200", "t-102"), teams))
# FLAG SHARING: flag belongs to team t-102
A few details matter here:
- Include the challenge ID in the message. Otherwise a team's flag would be the same across every challenge.
- Truncate sensibly. 24 hex characters give 96 bits, which is far beyond brute-force range and still short enough to paste.
- Compare in constant time.
hmac.compare_digestprevents timing side channels. CTF players are exactly the people who would try one. - Treat another team's flag as its own outcome. Don't just reject it: log it, alert and keep it for review. At scale, build the flag-to-team lookup once per challenge, not per submission.
CTF7 supports dynamic flags unique per team (per player in solo events). The snippet shows the general technique, not our internal implementation, but any sound approach needs the same properties: flags that can't be predicted or forged and can be traced to their owner.
Designing challenges for dynamic flags
Dynamic flags are only as good as the challenge delivering them, and some categories are harder to adapt than others.
Container challenges
The easiest case. When a challenge runs as a container, the deployment can inject a per-team flag through an environment variable (for example FLAG), and the challenge writes it wherever the player must reach. Make sure the player can't simply read the variable: clear it after startup or move it to a root-only file.
Static-file challenges
Forensics images, packet captures and jeopardy stego files are harder. Either generate one file per team (fine for small artifacts) or accept a static flag and lean on other signals. Track which challenges are dynamic so you know how much weight each leak alert deserves.
Crypto and reverse engineering
These can often be parameterized: encrypt a per-team plaintext, or compile the binary with the flag embedded at build time. When the math depends on a fixed puzzle, keep the puzzle and vary only the final flag.
Submission forensics: what the logs tell you
Your submission log is the record of what happened. Every attempt should store team, user, challenge, submitted value, timestamp, IP and browser fingerprint. Look for:
- Wrong-team flags. This is the strongest signal. The submitted value is a valid flag, just not the submitting team's.
- Timing correlation. Team B solves each challenge a few minutes after Team A, repeatedly, across unrelated categories.
- Solve-order similarity. Two teams solve the same unusual sequence of challenges, including the same ones they skipped.
- No failed attempts on hard challenges. A first-try correct flag on a 500-pointer from a team with no other activity in that category deserves a closer look.
| Signal | Strength of evidence | False-positive risk |
|---|---|---|
| Submitted another team's dynamic flag | Very strong | Very low |
| Honeypot flag submitted | Strong | Low |
| Same browser fingerprint across two teams | Moderate | Medium (shared lab machines) |
| Repeated solve-timing correlation | Moderate | Medium (popular challenges cluster) |
| Shared IP address | Weak on its own | High (campus NAT, venue Wi-Fi) |
| High submission volume | Weak (suggests brute force, not sharing) | Medium |
IP and browser signals, used carefully
CTF7's anti-cheating and monitoring tracks IPs and browsers, raises alerts and keeps full submission logs. These signals help, but you need to read them in context.
At a college CTF in India, hundreds of students may sit behind one campus NAT gateway, and at an on-site event the whole venue shares one Wi-Fi uplink. Disqualify teams for sharing an IP and you lose half your leaderboard.
Rule of thumb: Never penalize anyone on IP evidence alone. Use shared IPs and fingerprints to decide which logs to review, then back any decision with a stronger signal such as a wrong-team flag, a honeypot hit or clear timing correlation.
Browser fingerprints are more specific, but shared lab machines exist, so ask before assuming the worst.
Rate limits and brute-force defense
Some cheating involves no sharing at all: players script thousands of guesses or hammer the submission endpoint. Set per-challenge, per-team throttles, for example 10 attempts per minute with an increasing cooldown. A 96-bit dynamic flag can't be guessed anyway, so throttles mainly protect short static answers such as an OSINT city name. Log throttled attempts; repeat offenders are telling you something.
Honeypot and canary flags
A honeypot flag is a fake flag that looks real and is placed where only a cheater would find it. Examples:
- A "leaked" flag posted in a public paste or a fake writeup, which the platform treats as instant evidence when submitted.
- A decoy string in a challenge that looks like the flag but can only be reached through an unintended shortcut.
- A per-team canary shared in a private admin channel, to find out whether that channel is leaking.
Document honeypots before the event, and never plant one where an honest solver would naturally land.
Write an anti-cheat policy before you need one
Detection only works if the response is predictable and fair. CTF7's Terms of Service already prohibit sharing flags, solutions or detailed write-ups during an active competition, and using bots to gain an unfair advantage. Your published event rules should restate that in plain language and add:
- Rules. What counts as collaboration, whether AI tools are allowed (see should your CTF allow AI?), team-size limits, and one account per person.
- Evidence standard. Which signals lead to action (a wrong-team flag or a honeypot hit) and which only lead to a review (shared IP).
- Penalties. A graded scale: point deduction for the challenge involved, removal from prize eligibility, disqualification, and a ban from future events for repeat offenders.
- Appeals. A named contact and a deadline, for example 48 hours after results are announced, with a human review of the logs.
- Writeup embargo. No public writeups until the event closes, and afterward writeups are welcome.
Start from our free CTF rules template, which already covers flag sharing, writeup embargoes and tie-breaks, and pair it with a code of conduct so behavior rules and scoring rules sit side by side. Our guide to hosting a CTF covers where these fit in your event timeline.
On plans: Free covers static flags but not dynamic flags or anti-cheat; Standard (₹9,999 per event) includes both; Premium adds advanced anti-cheat analytics and logs. See pricing or tell us about your event.
Where to go next
- Definitions. Quick answers on what a dynamic flag is and how to set a flag format that validates cleanly.
- Write your policy. Copy wording from the CTF rules template, then decide your stance with should your CTF allow AI?
- Protect high-stakes events. Fairness matters most when results decide offers, so read hiring security talent with CTFs.
- Plan the whole event. How to host a CTF places anti-cheat in your event timeline.