adCTF is a comprehensive, open-source Attack Defense CTF platform designed to make hosting Attack Defense competitions simple and accessible. Whether you're organizing a small educational event or a large-scale competition, adCTF provides all the tools you need to manage teams, challenges, and scoring in real-time.
- 🔧 Easy Setup: One-command deployment using Docker Compose
- 👥 Team Management: Complete team registration and authentication system
- 🏆 Challenge Management: Deploy and manage challenge services effortlessly
- ⏱️ Round Configuration: Flexible tick-based scoring system
- 📊 Real-time Scoreboard: Live updates via WebSocket connections
- 🛡️ SLA Monitoring: Automated service availability checking
- 🔌 Participant Node: Isolated challenge environment for teams
- 📈 Admin Dashboard: Comprehensive competition management interface
- Docker & Docker Compose
- Python 3.8+
- Git
-
Clone the repository
git clone https://github.com/rayhanhanaputra/adCTF.git cd adCTF -
Configure environment
cp .env.example .env # Edit .env with your configuration -
Start the platform
docker-compose up -d
-
Access the platform
- Main Platform: http://localhost:5000
- Admin Dashboard: http://localhost:5000/admin
-
Prepare your server
# Download participant node git clone https://github.com/rayhanhanaputra/adCTF.git cd adCTF/participant-node
-
Start participant node
sudo python starter.py -u "admin_username" -p "admin_password"
adCTF Platform
├── Web Application (Flask)
│ ├── Admin Dashboard
│ ├── Scoreboard
│ ├── Team Management
│ └── API Endpoints
├── Database (MySQL)
├── Cache (Redis)
├── Challenge Services
└── Participant Nodes
Participant Node
├── Challenge Services (Docker)
├── SLA Checker
├── Flag Receiver
└── Service Monitor
Create a .env file in the root directory:
# Flask Configuration
FLASK_ENV=production
SECRET_KEY=your-secret-key-here
# Database Configuration
MYSQL_ROOT_PASSWORD=root_password
MYSQL_DATABASE=adctf
MYSQL_USER=adctf_user
MYSQL_PASSWORD=secure_password
MYSQL_PORT=3306
# Redis Configuration
REDIS_DB=0
# Admin Configuration
ADMIN_USERNAME=admin
ADMIN_PASSWORD=secure_admin_password
# Other Settings
SQLALCHEMY_TRACK_MODIFICATIONS=FalseConfigure your competition parameters:
- Tick Duration: Time between each scoring round (e.g., 60 seconds)
- Total Ticks: Number of ticks in the competition
- Total Rounds: Number of rounds in the competition
-
Team Management
- Register/deregister teams
- View team statistics
- Reset team credentials
-
Challenge Management
- Deploy challenge services
- Monitor service status
- Update challenge configurations
-
Round Configuration
- Set tick duration
- Configure scoring parameters
- Start/stop competition
-
Monitoring
- Real-time service status
- Team connectivity
- System performance metrics
Adding a new challenge to adCTF involves several steps. Critical: The challenge name must match exactly between all components.
Create your challenge service in participant-node/services/:
participant-node/services/YourChallenge/
├── docker-compose.yml (contains service definition)
├── Dockerfile
├── src/
│ ├── main.py
│ └── requirements.txt
└── writeup/
└── README.md
Add your service to participant-node/services/docker-compose.yml:
services:
notes: # Existing service
container_name: notes_container
# ... existing config ...
yourchallenge: # ⚠️ IMPORTANT: This name must match everywhere
container_name: yourchallenge_container
hostname: yourchallenge
restart: always
ports:
- "13000:8000" # Service port
- "13022:22" # SSH port
build:
context: YourChallenge
args:
- PASSWORD=$PASSWORD_13000
volumes:
- ../receiver/flags/yourchallenge.txt:/flag.txt:ro
- ../utils/bashrc:/root/.bashrc:ro
- ../utils/preexec.sh:/root/.preexec.sh:ro
extra_hosts:
- "host.docker.internal:host-gateway"Create participant-node/receiver/challenges/YourChallenge.py:
from .Challenge import Challenge
import requests
import secrets
import subprocess
class YourChallenge(Challenge):
flag_location = 'flags/yourchallenge.txt'
history_location = 'history/yourchallenge.txt'
def distribute(self, flag):
"""Writes the current flag to the specified location for the service to use."""
try:
with open(self.flag_location, 'w') as f:
f.write(flag)
with open(self.history_location, 'a') as f:
f.write(flag + '\n')
self.logger.info(f"Flag '{flag}' distributed successfully to {self.flag_location}")
return True
except Exception as e:
self.logger.error(f"Could not write flag to {self.flag_location}: {e}")
return False
def check(self):
"""SLA checker - verify service is working correctly"""
base_url = f"http://localhost:{self.port}"
session = requests.Session()
try:
# Basic connectivity check
r = session.get(base_url, timeout=5)
assert r.status_code == 200, f"Service down: {r.status_code}"
# Add your specific functionality checks here
# Example: test core features, authentication, etc.
# Verify flag is accessible in container
with open(self.flag_location, 'r') as f:
host_flag = f.read().strip()
container_flag = subprocess.run(
["docker", "exec", "yourchallenge_container", "cat", "/flag.txt"],
capture_output=True,
text=True
).stdout.strip()
assert host_flag == container_flag, 'Flag mismatch between host and container'
self.logger.info('SLA check passed for yourchallenge')
return True
except Exception as e:
self.logger.error(f'SLA check failed for yourchallenge: {e}')
return FalseUpdate participant-node/receiver/main.py:
# Import your challenge class
from challenges.YourChallenge import YourChallenge
# Add your challenge to the challenges dictionary
challenges = {
"notes": Notes(12000),
"yourchallenge": YourChallenge(13000), # ⚠️ Name must match docker service name
}Update participant-node/receiver/.env:
# Add password for your challenge port
PASSWORD_13000=your_generated_password_hereIn the main platform, add your challenge to the database via admin panel:
INSERT INTO challenge (name, title, port, description) VALUES
('yourchallenge', 'Your Challenge Title', 13000, 'Description of your challenge');Or use the admin web interface at /admin/challenges.
The challenge name MUST be identical in all these locations:
- Docker service name in
participant-node/services/docker-compose.yml - Challenge key in
participant-node/receiver/main.pychallenges dictionary - Challenge name in the main platform database
- Container name prefix (e.g.,
yourchallenge_container) - Flag file name (e.g.,
yourchallenge.txt)
webshop:
container_name: webshop_container
hostname: webshop
ports:
- "14000:8000"
- "14022:22"
# ... rest of configchallenges = {
"notes": Notes(12000),
"webshop": WebShop(14000), # Name matches docker service
}INSERT INTO challenge (name, title, port, description) VALUES
('webshop', 'Online Web Shop', 14000, 'Exploit this e-commerce application');- Service ports: Use increments of 1000 (12000, 13000, 14000, etc.)
- SSH ports: Service port + 22 (12022, 13022, 14022, etc.)
- Environment variables:
PASSWORD_{SERVICE_PORT}(PASSWORD_12000, PASSWORD_13000, etc.)
-
Build and start services:
cd participant-node/services docker-compose up -d yourchallenge -
Test the receiver endpoints:
cd participant-node/receiver python -m uvicorn main:app --reload --port 8001 # Test restart curl -u "admin:password" http://localhost:8001/restart/yourchallenge # Test SLA check curl -u "admin:password" http://localhost:8001/check/yourchallenge
-
Verify in main platform:
- Access
/challengesin the web interface - Verify restart/rollback buttons work
- Check that SLA monitoring functions correctly
- Access
- Network Isolation: Participant nodes should be properly isolated
- Resource Limits: Docker containers should have resource constraints
- Access Control: Implement proper authentication and authorization
- Monitoring: Log all administrative actions
- Backup: Regular database backups recommended
- Attack Points: Earned by successfully exploiting other teams
- Defense Points: Earned by maintaining service availability
- SLA Penalties: Deducted for service downtime
total_score = (total_attack_score + total_defense_score) * availability_score
availability_score = max(0.1, passed_checks / total_checks)
We welcome contributions from the community! Please see our Contributing Guidelines for details.
-
Clone and setup
git clone https://github.com/rayhanhanaputra/adCTF.git cd adCTF python -m venv venv source venv/bin/activate # Linux/Mac # or venv\Scripts\activate # Windows pip install -r requirements.txt
-
Setup the initial database
python init_db.py
-
Run in development mode
python run.py
- Follow PEP 8 for Python code
- Use meaningful variable and function names
- Add docstrings for public functions
- Write tests for new features
-
Database Connection Issues
- Verify MySQL is running
- Check database credentials
- Ensure database exists
-
Docker Compose Errors
- Update Docker and Docker Compose
- Check port availability
- Verify environment variables
-
Participant Node Issues
- Check network connectivity
- Verify credentials
- Review Docker service logs
-
Challenge Integration Issues
- 401 Unauthorized on restart/rollback: Check
ADMIN_USERNAMEandADMIN_PASSWORDmatch between main platform.envand participant-node.env - Challenge not found: Ensure challenge name matches exactly in docker-compose.yml, main.py, and database
- SLA check fails: Verify challenge class implements
check()method correctly - Flag distribution fails: Check file permissions and paths in challenge class
- Port conflicts: Ensure each challenge uses unique ports and they're not already in use
- 401 Unauthorized on restart/rollback: Check
-
Check challenge registration:
# In participant-node/receiver python -c "from main import challenges; print(list(challenges.keys()))"
-
Test challenge class directly:
python -c "from challenges.YourChallenge import YourChallenge; c = YourChallenge(13000); print(c.check())" -
Verify Docker service:
docker-compose ps yourchallenge docker-compose logs yourchallenge
-
Test API endpoints:
curl -u "admin:password" http://localhost:8001/check/yourchallenge
This project is licensed under the MIT License - see the LICENSE file for details.
- Thanks to all contributors and the CTF community
- Inspired by various CTF platforms and the need for simpler Attack Defense competitions
- GitHub Issues: Report bugs or request features
- Discussions: Community discussions
- Email: admin@siberlab.id
Made with ❤️ by SiberLab ID for the CTF community
