Point a class of Ubuntu VMs at a scoring server, let participants patch real vulnerabilities under a clock, and watch the leaderboard update live as each fix lands — automatically, no manual refresh, no spreadsheet grading. Free and open source, self-hosted on Vercel + Supabase.
⬇️ Quickstart · ✨ Features · ⚙️ How it works · 📖 Technical Design · 🧯 Troubleshooting ·
Most "cybersecurity competition" tooling is either offense-only (CTF flags, attack ranges) or a pile of manual work for organizers: someone has to walk around with a checklist, SSH into every VM, and grade hardening steps by hand. Scores land minutes or hours after the fact, and participants get no live feedback on what they've actually fixed.
BlueForge is a defensive (blue-team / system-hardening) competition platform, closer to CyberPatriot than to a red-team CTF. Participants receive a deliberately-vulnerable Ubuntu VM and a time limit; every genuine fix — SSH hardened, firewall enabled, rogue accounts removed, backdoors cleaned up — is detected automatically by a lightweight agent and reflected on a public leaderboard within seconds, no grader required.
- 🎯 Four difficulty tiers, 50 checks total — Easy (6) · Medium (30) · Hard (all 50, zero hints — a full dress-rehearsal tier) · FITCOM (all 50, sized for the real ~4h event), picked per session by the organizer. Checks run No. 1 → 50 in genuine easiest-to-hardest order, not just a random or insertion-order list — both the kiosk and the admin panel display that exact number next to every question.
- 💡 Hints that guide & instruct (30% hint policy) — 15 of the 50 checks carry a guiding
hint_basic(never a direct command/answer), distributed from Easy to Expert difficulty bands, while the remaining 70% stay hintless to encourage independent analysis. - 🧠 Black-Box State Verification — The agent inspects Linux system end-states (
check.py), NOT terminal command history or specific CLI tools. Participants have 100% freedom to use any command (sed,chmod,nano,vim, GUI tools, or custom scripts). Any valid method that achieves the security objective earns points automatically without debate or misperception. - ⚖️ Fair & evidence-backed anti-cheat — a Baseline & Evidence system snapshots each VM at registration/START/STOP, so fixing a check before START doesn't earn points (closes the "pre-fix" loophole). Pre-fix checks are visually marked with a red checkmark (
✔) + "Pre-fix" badge across the Admin Panel, Leaderboard, and Participant Kiosk UI. - 🚫 Disqualify with instant alerts & visual badges — clicking DQ freezes a participant's status: the agent stops scoring, an instant popup alert modal + persistent banner appears on the participant's VM kiosk, and the public Leaderboard displays a prominent red badge (
🚨 DIDISKUALIFIKASI) with strikethrough score. - ⚡ Truly live scoring — leaderboard polls + Supabase Realtime, admin
console auto-refreshes participant status, and the agent's clock-skew
correction means scores keep flowing even when a cloned VM's system clock
is wrong (see
docs/REVIEW-AND-CONCEPT-v2.md§2.1) — no more waiting on a manual VMware/Ubuntu clock refresh. - 🧩 Modular checks — add a new hardening check by dropping a
manifest.yaml+check.pyintoagent/checks/; the scoring engine never needs to change (plugin-style). - 🖥️ Zero-setup, crash-resilient participant experience — the agent
ships as a kiosk companion app (pywebview, with a browser-kiosk fallback)
that autostarts on VM boot: participants see registration, live score,
and remaining time without touching a terminal. Because participants hold
full
sudoon their own VM for the whole round, a crash orsudo rebootis expected, not exceptional — the agent persists its session locally and resumes automatically, and the server treats a matching re-registration as a resume rather than a rejection, so nobody gets locked out of their own progress (disqualified participants are still correctly blocked from this path). - 🔒 Signed, replay-resistant networking — HMAC-signed agent↔server traffic with a nonce store that rejects replayed requests, exponential backoff, and store-and-forward queuing so a flaky network during a competition never silently drops a score.
- 🛠️ Hardened organizer console — brute-force-limited admin login
(rate-limited, durably logged), a one-click CSV export of final
standings, and a full audit trail (
event_logs) of every disqualify, requalify, and removal. - 🆓 100% free stack — Vercel (web) + Supabase (Postgres/Realtime/Auth) + a pure-Python agent. No paid services required to run a competition.
Live demo — a participant's fix lands, the kiosk score updates instantly, and the public leaderboard catches up moments later — no manual refresh anywhere.
Leaderboard — live rank, score, and countdown, no refresh needed.
Detailed score checklist is available by clicking a participant on the public leaderboard:
Admin console — create sessions, start/pause/stop, manage participants, auto-refreshing.
Participant kiosk — a companion window (not fullscreen-lock), so participants can still use the terminal to work while registering and tracking their score live.
| Organizer (web) | Participant (per VM) | |
|---|---|---|
| Hosting | Free Vercel + Supabase project | — |
| OS | — | Ubuntu (20.04–26.04), provisioned via image/build/provision.sh |
| Runtime | Node.js 20+ (dev/build only — Vercel builds in the cloud) | Python 3.10+, root access (reads /etc/shadow etc.) |
| Network | Public HTTPS endpoint (Vercel) | Outbound HTTPS to that endpoint — agent only polls out, so it works fine behind NAT |
Pre-built VMware image — import and run, no manual provisioning needed.
| File | Link | Notes |
|---|---|---|
| 🖥️ BlueForge VM (VMware .ova Archive) | ⬇️ Download via Google Drive | 🔐 RAR Password: blueforge |
| 🔧 VMware Workstation Player | vmware.com/products/workstation-player | Free for non-commercial use |
Import the VM:
- Download & extract the
.rararchive using passwordblueforge. - Open VMware → File → Open → select the extracted
.ovafile. - Import → start the VM.
- Boot → BlueForge kiosk launches automatically. Fill in name + session code from your organizer.
Organizer only: after import, run
sudo bash ~/BlueForge/image/build/provision.shonce to plant all 50 intentional vulnerabilities before distributing the VM to participants.
- Create a free project at supabase.com.
- In SQL Editor, run
db/schema.sql, thendb/seed/difficulties.sql(brand-new project — this alone gives you all 4 tiers, including FITCOM). Upgrading an existing database from before the FITCOM preset existed? Run the relevant file(s) indb/migrations/first, then re-rundb/seed/difficulties.sql(it's UPSERT-based, safe to run again anytime). - Note your Project URL, anon key, and service_role key (Settings → API).
cd web
cp .env.example .env.local # fill in Supabase creds + AGENT_HMAC_SECRET
npm install
npm run dev # http://localhost:3000Deploy to Vercel: import the repo → set root directory to web/ → fill in
the environment variables.
cd agent
cp config.example.yaml config.yaml # set portal_url to your deployed web URL
pip install -r requirements.txt
sudo python3 main.py # open http://localhost:9090 to registerOr launch the kiosk companion app instead of the bare agent:
python3 kiosk.py (see docs/kiosk-setup.md for
autostart on VM boot).
- On
/admin: create a session, pick a difficulty (Easy/Medium/Hard/FITCOM), get a session code. - Participants register at
localhost:9090using that code. - Organizer clicks START → every agent begins scoring simultaneously → live scores on
/. - STOP freezes scores → export results.
🧭 First time self-hosting this end-to-end (repo → Supabase → Vercel → VM)? See the detailed beginner walkthrough:
docs/DEPLOYMENT-GUIDE.md.
| Component | What it does |
|---|---|
agent/ |
Python agent — runs checks, computes score, signs & sends results, serves the local kiosk UI |
web/app/api/v1/* |
Signed agent-facing API — register, state, score, heartbeat, snapshot, clock sync |
web/app/page.tsx |
Public live leaderboard |
web/app/admin/page.tsx |
Organizer console — sessions, participants, start/pause/stop, disqualify |
db/ |
Postgres schema + seed (difficulties, checks) + leaderboard view |
db/migrations/ |
Incremental SQL migrations for databases created before a schema change (e.g. adding the fitcom preset) |
image/build/provision.sh |
Plants all 50 intentional vulnerabilities into a base Ubuntu VM (idempotent — safe to re-run) |
[ participant VM: blueforge-agent ] --HTTPS (polling, signed)--> [ Next.js /v1 API ]
|
[ Supabase: Postgres + Realtime ]
|
[ Web: live Leaderboard + Admin console ]
The agent sits behind NAT and only polls out — the server never needs to
reach into a participant's VM. Every scoring cycle: sync clock with the
server (no manual clock fixing needed) → fetch competition state → run the
active checks → compute the score (pure function, eligible = failed at START) → sign and send. Full design in
docs/TECHNICAL-DESIGN.md.
- v0.2 — 15 checks across 3 real difficulty tiers, kiosk companion app, admin auto-refresh, agent clock-skew fix.
- v0.4 — 30 checks total (+15 new, including
root_home_perm), a dedicated FITCOM preset (all 30, sized for a ~2.5h event) with checks in genuine easiest-to-hardest order, guiding-only hints (never a ready-made fix command) on ~30% of checks, randomly distributed, instant DQ alert popups + leaderboard badges, red checkmark pre-fix indicators across all UIs, nonce anti-replay, admin login rate limiting, CSV export, and an agent that survives a participant's VM crashing or rebooting mid-round without losing their session. - v0.5 (current) — 50 checks total (+20 new: kernel/sysctl hardening,
sudoers
secure_path, planted SSH keys, hidden SUID backdoors, disguised listeners, privileged group membership). Tiers re-mapped: Medium now runs the old 30-check FITCOM set (No. 1–30), Hard and FITCOM both run the full No. 1–50 superset (identical content — Hard has zero hints for dress-rehearsal practice, FITCOM keeps the ~30% guiding-hint policy and a 4h duration for the real event).image/build/provision.shplants all 20 new vulnerabilities; verified intests/test_new_checks_sandbox.py. - Next up — richer plugin API for community-contributed checks, an evidence viewer UI, Windows participant VM support (agent port), and a Go rewrite of the agent for a smaller footprint.
- v1.0 — production hardening: multi-organizer orgs, PDF export, a
fuller admin audit UI on top of the
event_logstrail that already exists.
See docs/V0.2-PLAN.md, CHANGELOG.md,
and the full TDD roadmap (§29) for details.
Issues and PRs welcome — see CONTRIBUTING.md. Add a new
check by subclassing the run(ctx) -> {"passed": bool, "evidence": {...}}
contract in agent/checks/<code>/check.py; the engine needs no other change.
Found a vulnerability in the platform itself (not one of the intentionally planted training vulnerabilities)? Report it via SECURITY.md — please don't open a public issue.
Quick reference for the two operations organizers run most often on a
participant VM. Full step-by-step (Bahasa Indonesia) lives in
docs/DEPLOYMENT-GUIDE.md.
First run on a fresh VM clone:
cd ~/BlueForge
git pull
sudo bash image/build/provision.sh # plants all 50 intentional vulnerabilitiesprovision.sh clears ~/.bash_history (root & the login user) as its very
last step, so a cloned/exported VM doesn't leak the organizer's setup
commands to participants — no separate manual history-clearing step needed
anymore. If you keep typing commands in that same terminal after
provision.sh finishes and before exporting the VM, run history -c once
more right before export — bash can otherwise re-write the still-running
session's history back to the file on a normal shell exit.
Pulling a code update onto an already-installed VM — the kiosk/agent
that actually runs at boot is a separate copy installed to
/opt/blueforge-agent/ by install-kiosk.sh, not the git checkout itself.
git pull alone does not update what's running — you must resync:
cd ~/BlueForge
git pull
sudo bash agent/kiosk/install-kiosk.sh # resyncs code into /opt + restarts the service
sudo systemctl restart blueforge-agentKiosk window accidentally closed by a participant: kiosk.py now
auto-reopens the window on its own within ~2 seconds — no action needed. If
the window is ever truly stuck, double-click the "Restart BlueForge"
shortcut on the Desktop (no terminal required), or run
bash /opt/blueforge-agent/kiosk/restart-kiosk.sh.
Common diagnostic commands:
systemctl status blueforge-agent dhc-telnetd dhc-ftpd dhc-listener # are the services alive?
ps aux | grep -E "kiosk.py|main.py" # is the kiosk/agent actually running?
journalctl --user -b | grep -i -E "kiosk|webview|gtk" # kiosk autostart logs this boot
diff ~/BlueForge/agent/kiosk.py /opt/blueforge-agent/kiosk.py # in sync?This platform intentionally plants security vulnerabilities into VMs for training purposes. Isolated/air-gapped competition networks only — see DISCLAIMER.md before deploying.
MIT © 2026 Abil Khosim.
BlueForge is an original project by Abil Khosim, an independent security tool by Abil Khosim (NoxNull). Released under the MIT License — please keep this attribution when reusing or redistributing.
Stop collecting flags. Start patching real vulnerabilities. 🛡️