← Back to selected work

PHP · MySQL · Docker · Cloudflare · Security

OutRun CTF

A self-hosted capture-the-flag training platform where users register, work through five security challenges of increasing difficulty, and track their progress on a live dashboard and leaderboard — built, secured, and deployed end to end on my own hardware.

Role

Solo developer — app, database, security & infrastructure

Timeline

Course project, ongoing maintenance

Stack

PHP, MySQL, Docker, Nginx, Cloudflare

OutRun CTF landing page with Capture the Flag headline and Get Started button

The landing page — beginner-friendly framing, hints, and a leaderboard to climb.

Overview

OutRun CTF is a multi-page web application that simulates a simplified capture-the-flag training environment. Users register, log in, and work through five challenges covering common introductory security concepts — from inspecting hidden HTML to reading HTTP response headers. A dashboard tracks each user's points, solve rate, and global rank in real time.

The goal wasn't just to build a working app, but to demonstrate the full set of concepts behind a real dynamic web application: authentication, session management, secure database interaction, and a deliberately structured codebase — not just a collection of scripts that happen to work.

OutRun CTF registration form with username, name, country, and email fields

Registration and login, backed by hashed credentials and server-side sessions.

Application security

Security was treated as a first-class requirement, not an afterthought — every form and every user input is handled with a specific attack in mind.

  • Passwords hashed with bcrypt via password_hash(), verified with password_verify()
  • All queries run through PDO prepared statements to prevent SQL injection
  • Output escaped with htmlspecialchars() to prevent XSS
  • CSRF tokens on every form — login, registration, and flag submission
  • Session ID regenerated with session_regenerate_id() after login to prevent session fixation
  • Flag comparison uses hash_equals() for constant-time comparison, never ==
  • Rate limiting on login and flag submission (e.g. 10 wrong attempts per challenge per 5 minutes)
  • Flags are validated and stored server-side only — never exposed in page source or client-side JS
OutRun CTF dashboard showing points, rank, leaderboard, and challenge list

The dashboard — points, rank, a live leaderboard, and per-challenge status.

Dashboard & progress tracking

After logging in, users land on a dashboard that pulls everything dynamically from the database — no hardcoded stats. It's built to give an at-a-glance read on where someone stands, both against themselves and against everyone else.

  • Total points, challenges solved, and total attempts
  • A progress bar and challenge grid showing solved vs. unsolved
  • Global rank and a live leaderboard
Hidden in Transit challenge page with flag submission form and hint panel

Each challenge presents a problem, a flag submission form, and a hint on request.

Five challenges, five techniques

Each challenge teaches a specific, real-world reconnaissance or inspection skill. Submitted flags are validated server-side, and a correct submission is recorded against that user's account.

  • What Lies Beneath (easy) — finding a flag hidden in an HTML comment
  • No Robots (easy) — reconnaissance via robots.txt
  • Sweet Tooth (easy) — inspecting browser cookies
  • Encoded Secrets (medium) — decoding a Base64-encoded flag
  • Hidden in Transit (medium) — reading HTTP response headers

Architecture

The application follows an MVC structure. The Model layer handles all database interaction through PDO with prepared statements. The View layer manages the interface, built on a consistent design system of custom CSS variables rather than a framework. The Controller layer processes user input — form submissions, flag checks, session state — and ties the two together.

The database is normalized across six tables: users, challenges, categories, challenge_categories, user_challenges, and countries — separating user data, challenge content, categorization, and per-user progress so each piece can grow independently.

Entity-relationship diagram of the six-table OutRun CTF schema

Entity-relationship diagram of the six-table schema.

Terminal output of docker ps showing running containers on the home server

Containers running on the home server, confirmed via docker ps — the CTF app and database alongside other self-hosted projects.

Self-hosted infrastructure

OutRun CTF isn't hosted on a cloud VM — it runs on a Dell OptiPlex 390 I set up as a dedicated home server, upgraded with additional RAM and wired directly into the network. The whole deployment is managed through Docker.

  • Ubuntu Server with Docker and Docker Compose, containers set to restart: unless-stopped
  • Docker enabled to start on boot, verified after a full reboot test
  • Nginx reverse proxy in front of the app containers
  • Deploys by pulling from GitHub and running docker compose up -d over SSH
  • Blog posts published through the same headless SSH-key setup, no GUI required
Cloudflare dashboard overview for outrun-ctf.com showing traffic analytics

Cloudflare in front of the domain — DNS, proxying, and SSL.

Networking & domain

Running on a home connection means the network layer needed as much attention as the app itself: a stable path from a public domain name down to a single machine on a residential IP.

  • Registered domain, DHCP reservation for a stable local IP, ports 80/443 forwarded to the server
  • Cloudflare in front of the domain for DNS, free SSL, and DDoS protection — and to keep the home IP hidden
  • A DDNS script keeps Cloudflare's DNS record updated automatically if the home IP changes
  • Self-signed certificate generated with OpenSSL and configured in Apache, with Cloudflare set to Full (not Flexible) SSL mode end-to-end

Operations & hardening

Once the app was live, the focus shifted to keeping it that way — locking the server down, watching for downtime, and checking the site the way an attacker would rather than assuming it was secure.

Security research & writing

Alongside the platform itself, I wrote a set of technical posts covering the concepts the challenges are built on:

Outcome

The finished platform meets the full scope of the original proposal: secure authentication, a normalized database, an MVC codebase, five working challenges, and live progress tracking — running end to end on hardware I set up and manage myself, rather than a managed cloud platform. Handling the server, the network, and the security hardening alongside the application code meant thinking about the project as a whole system, not just the parts that live in a code editor.

Next project More Rocks →