NEXUS CTF Engine is a Jeopardy-style CTF platform foundation with ARC category gates, relaxed keyboard-first behavioral verification, adaptive scoring, live scoreboard updates, admin monitoring, Docker-based challenge deployment, shared redeem gateways, and dedicated nc runtime instances.
This README reflects the current deployment model:
- shared nc redeem for checker-style offline challenges
- dedicated nc runtime instances for real remote services, especially pwn
- live web services for web exploitation challenges
Best for lightweight offline challenges such as forensics, misc, and checker-based reverse or crypto tasks.
How it works:
- The downloadable artifact stays static in
backend/artifacts/... - The platform issues a short-lived redeem token
- The player connects to the shared redeem gateway with
nc - The gateway validates the recovered answer
- The gateway returns a time-windowed dynamic
NEXUS{...}flag - The backend validates that flag as a redeem-style dynamic flag
Use this when the challenge does not need to run a dedicated remote process per team or user.
Best for challenges that need a real remote process, such as:
- pwn
- reverse tasks with interactive remote checkers
- crypto tasks with a live socket service
- any custom TCP service that should actually run in its own container
How it works:
- The downloadable artifact stays static in
backend/artifacts/... - The player launches an isolated nc instance from the challenge page
- The backend builds the instance image automatically if needed
- The backend starts a private container for that team or user
- The challenge page shows the host, port, and short-lived instance access token
- After the player authenticates with the access token, the real challenge process starts
- The challenge process returns a stable runtime flag bound to that team or user
- The backend validates that flag as a runtime flag, not as a short TOTP window flag
This is the correct model for pwn.
Used for web exploitation challenges.
How it works:
- The platform launches the challenge with a signed short-lived access token
- The service returns a dynamic flag that matches backend validation
nexus-ctf-engine/
├── backend/
│ ├── app/
│ └── artifacts/
│ ├── reverse/
│ ├── pwn/
│ ├── crypto/
│ ├── forensics/
│ └── misc/
├── challenge_services/
│ ├── redeem_nc/
│ ├── nc_auth_exec/
│ └── web_ssti_notes/
├── frontend/
├── challenge_sdk/
├── docker-compose.yml
└── .env.example
Important runtime pieces:
redeem_nc/-> shared checker-style nc gatewaync_auth_exec/-> authenticated wrapper that launches the real runtime command after token verificationweb_ssti_notes/-> sample live web challenge
Use this for checker-style challenges where the player solves locally, then redeems the answer through the shared nc gateway.
- Place the downloadable artifact in:
backend/artifacts/<category>/<artifact>.zip
- Add a manifest next to it:
backend/artifacts/<category>/<artifact>.json
Example:
{
"slug": "forensics-150-lost-tabs",
"category": "forensics",
"artifact": "forensics/lost-tabs.zip",
"answer_prompt": "Recovered token from evidence",
"normalization": "strip",
"salt": "forensics-demo-v1",
"accepted_hashes": ["<sha256(salt:normalized_answer)>"],
"totp_interval": 120
}- Add the challenge metadata in:
backend/app/services/seed.py
When the player requests redeem info, the platform issues a short-lived token and the shared gateway validates the recovered answer.
Use this for real remote services, especially pwn.
The manifest still sits next to the static artifact, but now it contains a service block with kind = "nc_instance".
This is the most important mode for pwn and live socket challenges.
Example real pwn manifest:
{
"slug": "pwn-200-ret2win-mini",
"category": "pwn",
"artifact": "pwn/ret2win-mini.zip",
"service": {
"kind": "nc_instance",
"runtime_mode": "artifact_exec",
"owner_scope": "team",
"ttl_seconds": 1800,
"internal_port": 31337,
"build": {
"mode": "artifact_exec",
"dockerfile": "Dockerfile"
},
"template_context": "nc_auth_exec",
"files": ["ret2win-mini.bin"],
"notes": "Authenticate, then exploit the real remote binary to make win() print the live flag.",
"env": {
"SERVICE_NAME": "ret2win-mini",
"SERVICE_CATEGORY": "pwn",
"INSTANCE_BANNER": "[NEXUS pwn] ret2win-mini instance online. After authentication, the real vulnerable binary will start.",
"POST_AUTH_MESSAGE": "Access granted. Spawning the dedicated ret2win-mini process now...",
"CHALLENGE_COMMAND": "./ret2win-mini.bin"
}
}
}In this mode:
- the instance is not a redeem checker
- after token authentication, it starts the real challenge process
- for pwn, the player exploits that remote process over nc
- the remote process prints the live flag from the instance environment
artifact_exec also supports Python challenge services.
If a requirements.txt file exists in the same artifact directory, it is automatically included and installed during image build.
Example:
{
"slug": "crypto-200-xor-clock",
"category": "crypto",
"artifact": "crypto/xor-clock.zip",
"service": {
"kind": "nc_instance",
"runtime_mode": "artifact_exec",
"owner_scope": "team",
"ttl_seconds": 1800,
"internal_port": 31337,
"build": {
"mode": "artifact_exec",
"dockerfile": "Dockerfile"
},
"template_context": "nc_auth_exec",
"files": ["xor_clock_service.py"],
"notes": "This challenge runs a live Python service for your team.",
"env": {
"SERVICE_NAME": "xor-clock",
"SERVICE_CATEGORY": "crypto",
"INSTANCE_BANNER": "[NEXUS crypto] xor-clock instance online.",
"POST_AUTH_MESSAGE": "Access granted. Starting the dedicated Python challenge service...",
"CHALLENGE_COMMAND": "python3 -u xor_clock_service.py"
}
}
}Directory example:
backend/artifacts/crypto/
xor-clock.zip
xor-clock.json
xor_clock_service.py
requirements.txt
Notes:
requirements.txtdoes not need to be listed infiles- it is detected automatically if present
- use
python3 -u ...for cleaner interactive nc output
For web challenges, keep the live service in challenge_services/<name>/, add the challenge metadata in seed.py, and use a launch flow that returns a signed access token.
Use dedicated nc runtime instances.
Recommended pattern:
- artifact ZIP for download
- runtime binary beside the manifest
service.kind = "nc_instance"build.mode = "artifact_exec"template_context = "nc_auth_exec"CHALLENGE_COMMAND = "./your_binary.bin"
Do not use redeem-style EXPECTED_ANSWER for pwn.
You can choose either:
- shared nc redeem, if it is just a solved-answer checker
- dedicated nc runtime instance, if it should run a real interactive service
The default recommendation is:
- static artifact
- shared nc redeem
Authentication and players:
POST /api/v1/auth/registerPOST /api/v1/auth/login
Challenges:
GET /api/v1/challengesGET /api/v1/challenges/{id}GET /api/v1/challenges/{id}/downloadGET /api/v1/challenges/{id}/redeem-infoPOST /api/v1/challenges/{id}/instanceGET /api/v1/challenges/{id}/instanceDELETE /api/v1/challenges/{id}/instanceGET /api/v1/challenges/{id}/launch-url
Gameplay:
POST /api/v1/arc/submitPOST /api/v1/biometrics/calibratePOST /api/v1/biometrics/verifyPOST /api/v1/flag/submit
Scoreboard:
GET /api/v1/scoreboardWS /ws/scoreboard
Admin:
GET /api/v1/admin/monitoring/overviewGET /api/v1/admin/monitoring/usersGET /api/v1/admin/monitoring/teamsGET /api/v1/admin/monitoring/eventsPOST /api/v1/admin/users/{id}/banPOST /api/v1/admin/users/{id}/unbanPOST /api/v1/admin/teams/{id}/banPOST /api/v1/admin/teams/{id}/unban
cp .env.example .env
docker compose down -v
docker compose up --buildDefault local services:
- Frontend:
http://localhost:5173 - Backend API:
http://localhost:8000 - Swagger:
http://localhost:8000/docs - Shared nc redeem gateway:
nc localhost 2401 - Web sample challenge:
http://localhost:9105
Default admin account:
- Email:
[email protected] - Password:
admin123
The current build uses a relaxed, keyboard-first behavioral verification model.
Important behavior:
- typing signals are weighted more heavily than mouse movement
- behavioral verification is intended as a soft anti-abuse signal, not a harsh punishment mechanism
- repeated behavioral failures should not immediately auto-ban legitimate users
For local testing, if verification still feels too strict, lower:
BEHAVIORAL_THRESHOLD=0.05Then rebuild the stack and recalibrate.
Dedicated nc instance deployment uses the Docker socket from the backend container.
This is already wired in docker-compose.yml:
./challenge_services:/challenge_services/var/run/docker.sock:/var/run/docker.sock
Useful environment variables:
INSTANCE_PUBLIC_HOSTINSTANCE_NETWORKINSTANCE_PORT_STARTINSTANCE_PORT_ENDINSTANCE_ACCESS_TOKEN_TTL_SECONDSCHALLENGE_SERVICE_ROOT
There are now two flag validation modes:
These remain time-windowed dynamic flags.
These are stable runtime flags bound to the owning team or user and the challenge.
This is important for pwn and long-running runtime solves:
- the player may need time to exploit the service
- the flag should not expire just because the time window rolled over
- anti-sharing still works because the flag is bound to the correct owner and challenge
- Register a player and team
- Complete behavioral calibration
- Unlock the category with ARC
- Open the challenge page
- Download the static artifact
- Solve or analyze the artifact locally
- Click Launch nc instance
- Copy the host, port, and access token
- Connect with
nc host port - Paste the access token
- The real challenge process starts
- Trigger the solve condition in the running service
- Submit the returned
NEXUS{...}runtime flag in the platform
This build includes an admin monitoring dashboard at /admin/monitoring.
It provides:
- recent suspicious security events
- per-user and per-team 24-hour risk scoring
- automatic bans for severe token misuse and repeated suspicious bursts
- manual ban and unban controls for both users and teams
Behavioral verification failures should be treated more gently than hard token misuse.
- If behavioral verification is too strict for local testing, lower
BEHAVIORAL_THRESHOLDin.env - If a shared redeem token expires, request a new one from the challenge page
- If a dedicated nc instance fails to launch, confirm Docker is available to the backend and that the Compose network name matches
INSTANCE_NETWORK - If a Python runtime instance fails to start, check whether the artifact directory contains the required
requirements.txt - After changing seeded challenges or manifests, use
docker compose down -vand rebuild the stack
- pwn -> dedicated nc runtime instance with the real binary.
- reverse -> redeem checker or dedicated runtime instance, depending on whether it needs a live remote service
- crypto -> redeem checker or dedicated runtime instance, depending on the challenge design
- forensics -> shared nc redeem by default
- misc -> shared nc redeem by default
- web -> live web service