Challenge design
Guidelines for planning, authoring, and testing CTF challenges, including difficulty distribution, flag formats, and quality assurance.
Before you start authoring challenges, settle on the general format and scope of the competition. A well-organized CTF usually shares a few traits:
-
The event should last for either 24 or 48 hours and be held on a weekend or Friday night.
-
Challenges should conform to standard CTF categories.
-
Each category should contain approximately 2-6 challenges, with a similar number of challenges across all categories.
-
Challenge difficulty should be distributed across a range from easy to hard, accommodating participants of varying skill levels. A common distribution is 1-2 easy, 2-3 medium, and 1-2 hard.
Challenge authoring#
Good challenges are what make a CTF worth playing. Keep these guidelines in mind during development.
General principles#
-
Avoid recycling challenges from other CTFs or online resources. Participants can often identify reused challenges.
-
Avoid making challenges overly difficult, contrived, or reliant on “guessing.” A “guessy” challenge is one where the solution is unclear, so solvers fall back on arbitrary or brute-force approaches instead of reasoning from the given information. Good challenges have enough clues to make logical progress possible and reward problem-solving over brute persistence. If no one solves a challenge, the issue is usually design, not difficulty.
-
Challenges should be solvable within a reasonable timeframe given the event duration.
-
Challenge descriptions should not include red herrings.
-
Ideally, each challenge teaches or reinforces a concept or technique.
Challenge components#
Each challenge should include the following:
Dockerization#
For challenges that need a remote component, Docker is the recommended way to ship them. Containers give you reproducible builds, easy deployment, and isolation between challenges.
Best practices#
- Keep one challenge per container so each challenge stays self-contained. If a challenge requires multiple services (e.g., a web app and a database), use Docker Compose.
- Reach for minimal base images like
alpineordebian-slimto reduce build times and attack surface. - Build artifacts separately with multi-stage builds, compiling binaries in one stage and copying only the final artifact into the runtime image.
- Run challenge services as non-root users inside the container to limit the impact of container escapes.
- Use a read-only filesystem where possible by mounting the root filesystem as read-only (
readOnly: trueor--read-only) and using a tmpfs for writable directories.
Security defaults#
When deploying challenges, apply the following security settings:
cap_drop: ALLdrops all Linux capabilities.no-new-privileges: trueprevents privilege escalation.- Memory and PID limits prevent resource exhaustion (e.g., fork bombs).
- Network isolation uses internal networks for multi-container challenges where services should not be directly reachable.
Example Dockerfile#
FROM python:3.12-slim
RUN useradd -m ctfWORKDIR /home/ctf
COPY requirements.txt .RUN pip install --no-cache-dir -r requirements.txt
COPY app.py flag.txt ./RUN chmod 444 flag.txt
USER ctfEXPOSE 5000CMD ["python", "app.py"]Using with rCTF instancer#
If you’re using rCTF’s docker instancer integration, define the Docker Compose-like configuration in the challenge’s instancerConfig field. The instancer handles container lifecycle, networking, and cleanup automatically.
Pre-event checklist#
Before the event, verify the following for each challenge:
- The challenge is solvable with the provided materials. Where it fits, ship a single
solve.pythat outputs the flag. - The challenge has been playtested internally. Two playtesters per challenge is the safer bet.
- The flag is correct and matches the expected format.
- The files are correctly packaged and downloadable. Double-check that the files are the right versions, since authors sometimes update source code without re-packaging the distribution zip.
- The remote services are accessible and stable.
- The Docker containers build and run correctly.
- The solution or writeup is documented internally and accessible to the support team.
Tip (Recommended reading)
For additional guidance on challenge design, refer to the CTF Design Guideline.