- 🚩 What is UMassCTFd?
- 🚀 Quickstart - for challenge authors
- 🛠️ Installation & Deployment - for infra team
- 🎨 Extending UMassCTFd
UMassCTFd is an automated challenge + CTFd deployer used to provision and manage UMass Cybersecurity Club's CTFs and internal training platforms.
What is CTFd?
CTFd is an open-source Capture The Flag (CTF) platform used for hosting cybersecurity competitions, training platforms, etc. It provides a customizable interface and API for managing challenges, teams, scoring, and event logistics.
Challenge Categories:
Each challenge category will have its own subdirectory under /challenges
. All challenge directories must be placed in a subdirectory under /challenges/${CATEGORY}/
# Example Structure
/challenges/
├── crypto/
│ ├── challenge1/
│ ├── challenge2/
├── web/
│ ├── challenge1/
│ ├── challenge2/
The default challenge categories are:
- crypto
- forensics
- hardware
- misc
- pwn
- rev
- web
- OSINT
To add a new category, you can just create a new subdirectory under /challenges
.
Challenge Contents:
We support automated challenge deployments with:
- Static assets (e.g., downloadable files)
- Running services (e.g., websites, APIs, network services) Both or neither
- More advanced setups, including multi-container deployments (e.g., databases, message queues, or custom infrastructure)
Environments:
We maintain two environments: dev and prod, each linked to a corresponding GitHub branch. Following GitOps best practices 🤓, all challenges should be tested in dev before being promoted to prod.
Note
The prod environment will only be set up ~1 week before the CTF to minimize infrastructure costs.
This repository serves as the single source of truth for all challenges.
It contains both:
- Challenge metadata (e.g., descriptions, points, etc.)
- Source code
Warning
Manual changes in CTFd will be overwritten by our CI/CD pipelines.
All deployments and updates must be done through this repo!
(We plan to add support for editing challenge data in CTFd directly soon!)
Our CTFd instances and all challenges are hosted on GCP. The diagram below provides a high-level overview of our infrastructure:
Important: We have three example challenges under
/challenges/examples/
. Please use them as a reference!
Important
Flag Format: UMASS{FLAG}
Ensure your challenge name follows this regex pattern:
^[A-Za-z][A-Za-z0-9-]*$
-
Create a new branch for your challenge using the format:
${CHALLENGE_CATEGORY}/${CHALLENGE_NAME}
-
Create a new directory for your challenge within the appropriate category directory:
/challenges/${CHALLENGE_CATEGORY}/${CHALLENGE_NAME}
-
Each challenge directory must include the following files:
-
Required:
-
info.yaml
: Stores the challenge metadata used for the CTFd entry, including details like the challenge's display name, point value, flags, etc.
This file is directly used to create or update your challenge in CTFd. For complete documentation on structuring this file, refer todocs/info.yaml
anddocs/full-info.yaml
. -
Optional (depending on challenge type):
-
solve.py
: An automated script that replicates the steps required to solve a challenge, including sending requests, performing exploits, or processing data to retrieve the flag. -
Static downloadable assets:
If the challenge includes static downloadable assets (e.g., images, source code files), create astatic
subdirectory to store them. -
Running services:
If the challenge requires running services (e.g., a website, interactive script), include adocker-compose.yaml
file.Need a separate instance for each connection?
If your service should start a fresh instance per user connection (e.g., for binary exploitation or sandboxed environments), use
ynetd
in yourDockerfile
.
ynetd
ensures each connection gets its own isolated process without reusing state from previous users.# Example FROM debian:latest RUN apt update && apt install -y ynetd COPY challenge_binary /challenge CMD ["ynetd", "-p", "1337", "-u", "nobody", "--", "/challenge"]
Running multiple services?
If your challenge requires multiple services (e.g., a database, chat bot, API, etc.), you can define them in your
docker-compose.yaml
file.
This allows you to specify how different containers interact with each other.# Example version: "3" services: web: build: ./web ports: - "8080:80" db: image: mysql:latest environment: MYSQL_ROOT_PASSWORD: root MYSQL_DATABASE: challenge_db bot: build: ./bot depends_on: - db
In this example:
web
is a service running a website.db
is a MySQL database.bot
is a chatbot that depends on the database.
To choose a public port for your services:
Each category has a predefined public port range:
- Pwn:
30000-31000
- Crypto:
40000-41000
- Web:
50000-51000
- Other:
60000-61000
To expose a port publicly in a Docker Compose file, use the
ports
directive in the service definition.# Example services: myapp: image: myapp:latest ports: - "8080:80" # Maps port 80 inside the container to port 8080 on the host, making it accessible publicly.
Assign unique ports: All challenges must use a unique port within their category's assigned range. To prevent conflicts:
- Update the port-tracker file by adding your challenge’s assigned port to
docs/port-tracker.md
. - Ensure that no other challenge is already using the same port.
-
-
-
Create a Pull Request (PR) for your branch.
-
Ensure all pre-merge checks have passed before deploying your challenge.
-
Get approval if required.
- Permissions are based on your team assignment:
UMassCTF25-Admins
andUMassCTF25-Challenge-Authors
. - If you're in the
UMassCTF25-Challenge-Authors
group, you must get approval from anUMassCTF25-Admins
before merging your PR. - You can find the admin list here.
- Permissions are based on your team assignment:
-
Merge your PR into the
dev
branch. -
Verify the
challenge-update
pipeline passes
- Our "upload" pipeline is responsible for deploying your challenge to the GCP VM and updating or creating an entry in CTFd using the details from
info.yaml
. To ensure everything works correctly, check if this pipeline succeeds. - You can monitor the workflow run on the Actions page—look for a job named "Challenge Update", which corresponds to the last commit message in your PR:
- If the pipeline fails, review the error messages provided in the workflow logs. These messages are detailed and will help you troubleshoot any issues.
-
After playtesting and finalizing your challenge in our dev CTFd environment, create a new PR to promote to the
prod
branch.- Repeat steps 1-3, then merge your PR to the
prod
branch.
- Repeat steps 1-3, then merge your PR to the
-
Delete your challenge's branch.
Important
Connecting to our dev environment requires a WireGuard VPN connection. We'll be setting this up in the next few days and updating the README with connection instructions.
The dev CTFd environment is where we can test, iterate on challenges, and make changes before promoting them to the production CTFd environment.
-
Navigate to:
34.139.68.169
-
Create an account in the dev CTFd environment using the registration code below:
- Registration Code:
D3v0psCluB_1s_Bett3r_C0de!6969
- Dev CTFd
admin
Credentials:- Username:
admin
- Password:
D3v0psCluB_1s_Bett3r!
- Username:
- Registration Code:
- Check the playtesting spreadsheet for your assigned tasks:
Playtesting Status & Assignments - Complete your assigned tests and update the status accordingly.
- If anything is unclear or broken, leave comments in the spreadsheet and let the challenge author know.
Warning
🚨 Do not edit your challenges directly in the dev
or prod
branches, even if you have the necessary permissions. Doing so can cause unintended issues, and your changes may not be properly deployed. Always make edits through pull requests (PRs)! 🚨
- Use the branch you originally created to make edits to your challenge.
- Follow the deployment steps again to push your changes.
🚧 This documentation is still under construction! 🚧
🚧 This documentation is still under construction! 🚧