Challenges are the core of any CTF competition. GZCTF supports multiple challenge types with sophisticated flag validation and container orchestration.
Challenge Types
GZCTF supports five distinct challenge types:
Static Attachment Single attachment file with fixed flags. Perfect for reverse engineering, forensics, or cryptography challenges.
Dynamic Attachment Unique attachment per team generated from templates. Prevents flag sharing between teams.
Static Container Shared Docker container with fixed flags. Suitable for web exploitation or network services.
Dynamic Container Individual container per team with unique flags. Maximum isolation and fairness for pwn/web challenges.
Static Content No files or containers—challenge info is in the description. Used for OSINT or trivia.
Creating Challenges
Navigate to Game Challenges
Open the game in admin panel, go to Challenges tab, and click Add Challenge .
Set Basic Properties
Challenge name displayed to participants
Category classification:
Web - Web exploitation
Pwn - Binary exploitation
Reverse - Reverse engineering
Crypto - Cryptography
Forensics - Digital forensics
Misc - Miscellaneous
Hardware - Hardware hacking
Mobile - Mobile security
PPC - Programming challenges
Blockchain - Web3/blockchain
AI - Artificial intelligence
Challenge type (see above section)
Configure Challenge Content
Markdown-formatted challenge description, hints, and connection information
Scoring Configuration
Dynamic Scoring System
GZCTF implements dynamic scoring where challenge points decrease as more teams solve them:
Initial point value before any solves
Minimum score as a fraction of original (e.g., 0.4 = 40% minimum)
Controls how quickly score decreases. Higher values = slower decay.
Score Calculation Formula
def calculate_score ( original_score , min_score_rate , difficulty , solved_count ):
"""
Calculate dynamic challenge score based on solve count
"""
min_score = original_score * min_score_rate
current_score = (
(original_score - min_score) *
(difficulty / (solved_count + difficulty)) +
min_score
)
return max ( int (current_score), min_score)
# Example: 500pt challenge, 40% min, difficulty 3
print (calculate_score( 500 , 0.4 , 3.0 , 0 )) # 500 (no solves)
print (calculate_score( 500 , 0.4 , 3.0 , 3 )) # 350 (3 solves)
print (calculate_score( 500 , 0.4 , 3.0 , 10 )) # 238 (10 solves)
print (calculate_score( 500 , 0.4 , 3.0 , 50 )) # 200 (minimum)
Use higher difficulty values (5-10) for easy challenges to keep them valuable longer. Use lower values (1-2) for hard challenges.
Blood Bonus
Disable blood bonus for this specific challenge (bonus still applies to other challenges)
Flag Management
Flags are the answers teams submit to earn points.
Static Flags
For static challenge types, add one or more flags manually:
Add Flag
Click Add Flag in the challenge edit page
Configure Flag
The flag value (e.g., flag{this_is_a_flag})
Text - Exact string match (case-insensitive)
RegExp - Regular expression pattern
Example: Pattern Flag
Example: Text Flags
# Accept multiple formats
flag \{ [ a-f0-9 ] {32}\}
# Flexible spacing
flag \{ \s * welcome [ _ \s ] * to [ _ \s ] * gzctf \s *\}
Dynamic Flags
For dynamic container/attachment challenges, use flag templates:
Template with placeholders for unique flag generation:
[TEAM_HASH] - Unique team identifier
[GAME_ID] - Game identifier
[CHALLENGE_ID] - Challenge identifier
# Basic team-unique flag
flag{[TEAM_HASH]}
# Complex format
flag{team_[TEAM_HASH]_game_[GAME_ID]_challenge_[CHALLENGE_ID]}
# Realistic-looking flags
GZCTF{[TEAM_HASH]_c0ngr4tul4t10ns}
Flag templates must not be too simple. The system validates that [TEAM_HASH] is included to prevent static flags disguised as dynamic ones.
Container Configuration
For container-based challenges (Static/Dynamic Container):
Container Image
Docker image name with tag (e.g., gzctf/web-challenge:latest)
Prepare Image
Build your challenge image and push to a container registry (Docker Hub, GHCR, private registry)
Configure Exposed Port
Port your service listens on inside the container
Resource Limits
Prevent resource exhaustion:
Memory limit in MB (range: 32-1048576)
CPU limit in 0.1 CPU units (e.g., 10 = 1.0 CPU, 5 = 0.5 CPU)
Disk storage limit in MB (range: 0-1048576)
Example: Lightweight Web Challenge
Example: Heavy PWN Challenge
ContainerImage : myregistry/web-chall:v1
MemoryLimit : 128 # 128 MB RAM
CPUCount : 5 # 0.5 CPU cores
StorageLimit : 512 # 512 MB disk
ExposePort : 80
Network Mode
Open - Full internet access
Isolated - No external network access
DNS - DNS resolution only
Use Isolated for challenges that don’t need internet to prevent container escape or exfiltration attacks.
Traffic Capture
Record all network traffic for forensics/monitoring. Only available for container challenges.
Captured traffic is stored as PCAP files accessible to admins for cheat detection or debugging.
Attachment Management
Static Attachments
For Static Attachment/Container challenges:
Upload File
Click Update Attachment and select your file
Configure Display Name
Remote - File on external server (provide URL)
Local - File stored in GZCTF
Dynamic Attachments
For Dynamic Attachment challenges:
Base filename for generated attachments (e.g., challenge.zip)
Prepare Template Package
Create a ZIP archive containing:
Challenge files
Template files with [TEAM_HASH] placeholders
Upload Template
System generates unique attachments per team by replacing placeholders
Advanced Features
Submission Limits
Maximum flag submissions per team (0 = unlimited). Useful for preventing brute-force attacks.
Example: Guessing Challenge
SubmissionLimit : 5 # Only 5 attempts allowed
Challenge Deadline
Optional deadline before game end. Challenge becomes inaccessible after this time.
Hints System
Progressive hints revealed at scheduled times or costs
Add Hints
Add multiple hint strings in the challenge editor
Automatic Notification
When hints are updated during an active game, a system notice is automatically published
Testing Challenges
Container Test Mode
Before enabling a challenge, test containers:
Create Test Container
Click Test Container button in challenge editor
Verify Functionality
Check service responds on exposed port
Verify dynamic flags are correctly generated
Test flag submission
Destroy Test Instance
Click Destroy Test Container when testing is complete
Test containers use admin resources and count toward your container quotas. Remember to destroy them after testing.
Enabling Challenges
Make challenge visible and solvable by participants
Validate Configuration
Before enabling, system checks:
At least one flag exists (for non-dynamic challenges)
Container image is configured (for container challenges)
Flag template is valid (for dynamic challenges)
Enable Challenge
Toggle IsEnabled to true
Container Initialization
For container challenges, system pre-creates instances for faster deployment
When enabled during an active game, a system notice “New challenge available: [Title]” is automatically broadcast.
Bulk Operations
Export/Import Games
Export entire games including all challenges:
curl -X POST https://your-gzctf.com/api/edit/games/1/export \
-H "Authorization: Bearer YOUR_TOKEN" \
-o game-backup.zip
Delete Challenge
Deleting a challenge also removes:
All flags
Submission history
Container instances
Team progress for that challenge
This action is irreversible .
API Reference
POST /api/edit/games/{id}/challenges
Create new challenge. Returns challenge ID and details.
GET /api/edit/games/{id}/challenges/{cId}
Get detailed challenge configuration including flags (except dynamic container flags).
PUT /api/edit/games/{id}/challenges/{cId}
Update challenge settings. Flags are managed separately via flag endpoints.
DELETE /api/edit/games/{id}/challenges/{cId}
Permanently delete challenge and all related data.
POST /api/edit/games/{id}/challenges/{cId}/flags
Add one or more flags to the challenge.
DELETE /api/edit/games/{id}/challenges/{cId}/flags/{fId}
Remove a specific flag.
POST /api/edit/games/{id}/challenges/{cId}/attachment
Upload or update challenge attachment.
POST /api/edit/games/{id}/challenges/{cId}/container
Create test container instance.
DELETE /api/edit/games/{id}/challenges/{cId}/container
Destroy test container instance.
Best Practices
Test Thoroughly Always test challenges before enabling:
Verify flags work
Check container stability
Test resource limits
Set Appropriate Resources Balance between:
Challenge needs
Infrastructure capacity
Participant experience
Use Dynamic Flags For serious competitions, use dynamic flags to prevent:
Flag sharing
Early leaks
Unfair advantages
Clear Descriptions Include in challenge content:
Connection information
Expected flag format
Difficulty hints
Author credits
Troubleshooting
Check error message:
“No flags”: Add at least one flag
“Container config error”: Verify image and port settings
“Flag template invalid”: Ensure [TEAM_HASH] is included
Verify:
Image exists in registry
Registry credentials configured (if private)
Resource limits are reasonable
Container provider is running
Dynamic flags not working
Confirm:
Flag template includes [TEAM_HASH]
Challenge type is Dynamic Container/Attachment
Container/attachment generates flag correctly
Attachments not downloading
Check:
File was uploaded successfully
Storage backend is accessible
File permissions are correct
Next Steps
Team Management Manage participant registrations
Monitoring Monitor competition progress