Warning
The vm has been shut down and the subdomain name deleted (ctfman)!
Table of Contents
The goal of CTFman - a web application using a database - is to efficiently manage Capture The Flag (CTF) competitions. The database will store and organize information about CTFs, participants, teams, tools, and performance metrics. This will allow for:
- Historical Tracking: Preserving records of competitions, which is essential since CTFs are only accessible for a limited time during the event duration.
- Data Interaction: Providing a seamless interface to visualize, modify, and manage the stored data.
- Comprehensive Functionality: Enabling functionalities like managing team registrations, tracking user activities, organizing challenges, managing tools and resources, and moderating comments.
Note
CTF - Capture The Flag - is a competition where participants solve cybersecurity challenges to gain points and compete against others.
Caution
Hacking is not necessarily a negative practice. In fact, it plays an essential role in learning and discovering existing vulnerabilities in computer systems. Through disciplines like CTF (Capture the Flag), cybersecurity enthusiasts can develop skills by identifying potential flaws and understanding how they could be exploited. This knowledge is then used to strengthen system security and prevent future attacks. Thus, ethical hacking or "white hat" hacking actively contributes to the protection of digital infrastructures by anticipating risks and proposing solutions.
-
# Install the plugin if needed asdf plugin add java # Install asdf install java latest:temurin-21
-
Mac (homebrew)
brew tap homebrew/cask-versions brew install --cask temurin@21
-
Windows (winget)
winget install EclipseAdoptium.Temurin.21.JDK
If are a standard user go here.
Clone this repository with either ssh or http, but we recommend using ssh as it is more secure.
git clone git@github.com:shadowkudo/ctfman.gitUse the maven wrapper to install dependencies, build and package the project.
# install the dependencies
./mvnw clean install
# build
./mvnw package
# run
java -jar target/<filename>.jar --helpBuild the Docker image and publish it to GitHub Container Registry:
docker build -t ghcr.io/<username>/ctfman:latest
docker push ghcr.io/<username>/ctfman:latestNote
We used GitHub Actions so that it publishes automaticaly when new commit is done in main branch.
Run the application using Docker Compose:
docker compose --profile dev up -dIn our project, we choose to use Microsoft Azure as the provider for the virtual machine for our web server. See below for the installation and configuration process for a student account.
Access the Azure portal with the following link: https://portal.azure.com and log in.
Create a new virtual machine from the dashboard in section Create a resource.
Select a virtual machine with the following characteristics:
- Project details
- Subscription: Azure for Students
- Resource group: Create new with the name
heig-vd-dai-course
- Instance details
- Virtual machine name:
heig-vd-dai-course-vm - Region: (Europe) West Europe
- Availability options: No infrastructure redundancy required
- Security type: Trusted launch virtual machines (the default)
- Image: Ubuntu Server 24.04 LTS - x64 Gen2 (the default)
- VM architecture: x64
- Size:
Standard_B1ms- you might need to click "See all sizes" to see this option
- Virtual machine name:
- Administrator account
- Authentication type: SSH public key
- Username:
ubuntu- please use this username so the teaching staff can help you if needed - SSH public key source: Use existing public key
- SSH public key: Paste your public key here - see the note below for more information
- Inbound port rules
- Public inbound ports: Allow selected ports
- Select inbound ports: HTTP (80), HTTPS (443), SSH (22)
Caution
The server needs to have at least 2 GiB RAM so that it will not freeze ! You may also add a swap file of 1 GiB to be more secure on memory usage.
Click on the Review + create button.
Validate the configuration and click on the Create button.
It might take a few minutes to create the virtual machine. Once the virtual
machine is created, you can access it with the Go to resource button.
Note the public IP address of the virtual machine. You will need it to connect to the virtual machine with SSH later.
In this section, you will access the virtual machine with SSH and configure it.
Using the public IP address of the virtual machine, you can connect to the virtual machine with SSH with the following command:
# Connect to the virtual machine with SSH
ssh ubuntu@<vm public ip>The first time you connect to the virtual machine, you will be asked to confirm the fingerprint of the public key of the virtual machine.
The output should be similar to the following:
The authenticity of host '104.45.17.214 (104.45.17.214)' can't be established.
ED25519 key fingerprint is SHA256:Xl0X5kv+aeZV28XA9iJ/L+geFVVvOvG4foRixbGRYnY.
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])?
You can type yes and press the Enter key.
Once connected to the virtual machine, you can update the packages with the following command:
# Update the available packages
sudo apt update
# Upgrade the packages
sudo apt upgradeYou can then reboot the virtual machine with the following command to apply all the updates:
# Reboot the virtual machine
sudo rebootInstall and configure Docker and Docker Compose on the virtual machine. You will need them to run our web application.
Congratulations! You have now an up-to-date and configured virtual machine to use.
For the server to be accessible easily on the Internet, you need to set up a DNS - Domain Name Server - so that you will use a name instead of a ip address to access the server remotely.
You need to either buy a domain name (like we did using Polisystems) or use a free domain name (see Duckdns).
Add an A record to the DNS zone of your domain name provider to point to the IP address of the virtual machine.
Add a second wildcard A record to the DNS zone of your domain name provider to point to the IP address of the virtual machine. This will allow access to all your services hosted under a subdomain of your domain name.
Test the DNS resolution of the DNS records you added from the virtual machine and from your local machine.
# Test the DNS resolution
nslookup ctfman.cybernest.chOn success, the output should be similar to the following:
Server: 192.168.247.244
Address: 192.168.247.244#53
Non-authoritative answer:
Name: ctfman.cybernest.ch
Address: 104.45.17.214On failure, the output should be similar to the following:
Server: 192.168.247.244
Address: 192.168.247.244#53
** server can't find heig-vd-dai-course.duckdns.org: NXDOMAINNote
You might have to wait a few minutes (max 15 minutes in our experience) for the DNS record to be propagated and get a successful response. In case you use a WIFI to connect to the Internet, it may take longer if their cache take more time to reset (if during your configuration you made an error and had to update it)!
With Swagger, documentation is created during coding; see the endpoint https://api.ctfman.cybernest.ch/swagger or https://api.ctfman.cybernest.ch/redoc to look at the API documentation.
Note
It is recommended to use swagger as you can try the API easily.
- Starting the database
docker compose up db -d- Setting up the database and populate it with data
docker compose run --rm prod setup --seed- Starting the web application
docker compose up prod -d- Accessing the application in a browser (tested with
Firefox)
firefox https://ctfman.cybernest.chImportant
You can go directly to step 4, as you do not have access to the server. In case a problem occur, create an issue here with the tag bug and we will try our best to support you!
If not yet installed, do (for Ubuntu systems):
# Install curl
sudo apt install curlcurl -i -X 'POST' \
'https://api.ctfman.cybernest.ch/login' \
-H 'accept: */*' \
-H 'Content-Type: application/json' \
-d '{
"username": "user1",
"password": "password"
}'Will get:
HTTP/2 204
content-type: text/plain
date: Fri, 17 Jan 2025 16:57:54 GMT
expires: Thu, 01 Jan 1970 00:00:00 GMT
set-cookie: session=HTOsOvSX0eEPXaAwy76Zjwjaadim9bWeD4-DkOMVjqg; Path=/; Secure; SameSite=Nonecurl -X 'GET' \
'https://api.ctfman.cybernest.ch/profile' \
-H 'accept: application/json' \
-H 'Cookie: HTOsOvSX0eEPXaAwy76Zjwjaadim9bWeD4-DkOMVjqg'Important
As seen here, you need to add the cookie from the login response to all the future request as you need to be logged in to access other information. The only exception would be to logout as it will not give confidential information.
Will get:
HTTP/2 200
content-type: application/json
date: Fri, 17 Jan 2025 17:09:47 GMT
content-length: 188
{"createdAt":"2025-01-13T12:49:41Z","deletedAt":null,"authentication":"user1","primaryContact":"user1@example.com","isChallenger":true,"isAdmin":false,"isModerator":false,"isAuthor":false}curl -i -X 'POST' \
'https://api.ctfman.cybernest.ch/logout' \
-H 'accept: */*' \
-d ''Will get:
HTTP/2 204
content-type: text/plain
date: Fri, 17 Jan 2025 16:58:16 GMT
expires: Thu, 01 Jan 1970 00:00:00 GMT
set-cookie: session=; Path=/; Expires=Thu, 01-Jan-1970 00:00:00 GMT; Max-Age=0Note
I think with those examples, you understood how it works. For others endpoints it will work the same way; be careful to never forget the cookie in the request !
The following examples have been made with Firefox.
Login:
Logout:
Navigation:
List teams:
Edit team:
Create team:
Team details:
Note
It will work the same way for other field like CTF.
Contributions are what make the open source community such an amazing place to learn, inspire, and create. Any contributions you make are greatly appreciated.
- Fork the Project
- Create your Feature Branch (
git checkout -b feature/AmazingFeature) - Commit your Changes (
git commit -m 'Add some AmazingFeature') - Push to the Branch (
git push origin feature/AmazingFeature) - Open a Pull Request
Distributed under the MIT License. See LICENSE for more information.
Important
Have fun !







