A practical reference for running the platform locally: prerequisites, quickstart, port configuration, architecture, challenge and lab mechanics, and flag format. Keep this open in a second tab while you build.
Breachforge runs entirely in Docker. One command brings the full stack online: nginx reverse proxy, three language runtimes (PHP 8 + Apache, Python 3 + Flask, Node.js 20 + Express), MySQL, MongoDB, and every challenge + lab container. Nothing installs on your host beyond Docker itself.
Breachforge is a training target. The techniques you learn here are only legal to use against systems you own or have written permission to test. The platform ships with its own deliberately vulnerable services — do not point exploit tooling at anything else.
docker compose version should work).crypto.subtle for flag hashing — Chrome, Firefox, Safari, and Edge all work.Clone the repo, then bring the stack up with a single command. The first run pulls base images and seeds MySQL/MongoDB, which takes a minute or two.
# clone git clone https://github.com/your-org/breachforge.git cd breachforge # build + run everything docker compose up -d --build # follow logs docker compose logs -f
Once it settles, open http://localhost:8080 in your browser. You should see the Breachforge hub. Click through to Challenges to launch single-vulnerability targets, or Labs for multi-stage attack chains.
The hub binds to 8080 by default. If you already have something on that port, override it with the BREACHFORGE_PORT environment variable before running compose:
# one-shot override BREACHFORGE_PORT=9090 docker compose up -d # or persist it in .env echo "BREACHFORGE_PORT=9090" >> .env docker compose up -d
Internal service ports (MySQL 3306, Mongo 27017, individual challenge containers) are not exposed to the host. Everything is routed through the nginx proxy at BREACHFORGE_PORT.
Breachforge is a single docker-compose.yml that orchestrates four layers:
/ to the frontend, /api/* to backend services, and /chain/NN/* to lab services./chain/NN/ with their own databases, admin bots, and internal metadata endpoints.Both databases (MySQL, MongoDB) are shared between challenges for efficiency. Labs that need clean state spin up their own DB instances.
The Challenges page lists 30 single-vulnerability targets. Each one isolates a single technique — SQLi, SSRF, XXE, prototype pollution, deserialization, etc. — so you can focus on the exploitation primitive without solving a whole puzzle to reach it.
Click a challenge card to open the detail panel, pick a difficulty (Low / Medium / High — same bug, different hardening), and hit Launch. The target opens in a new tab served through the proxy. Exploit it, pull the BF{…} string out, and come back to submit.
Labs chain multiple bugs into a realistic kill path. Lab 04 (MicroHub), for example, asks you to chain NoSQLi → SSRF → zip-slip → IDOR to pull a treasury user's premium invoice. No lab is solvable with one bug.
Each lab card on the Labs page has a flag-submission input at the bottom. Paste the BF{…} string you recover at the end of the chain and hit submit. The page hashes your input with SHA-256 in your browser and compares against the expected hash baked into the page — so the flag is never visible in HTML source. Successful submissions are stored in localStorage under the key breachforge.captured.labs.
To clear your captured labs, open devtools on the Labs page and run localStorage.removeItem('breachforge.captured.labs'). Refresh — all labs reset to uncaptured.
Ten challenges per stack. Pick the one you want to train against:
Include/LFI, SQL injection, unrestricted upload, session fixation, deserialization, XXE — the canonical LAMP attack surface.
SSTI, SSRF via requests, pickle deserialization, JWT misuse, directory traversal, weak crypto.
Prototype pollution, NoSQL injection, eval sinks, JWT alg=none, open redirect, race conditions.
All flags follow the shape BF{…} — a 12-character HMAC-derived hex string. For example, BF{a1b2c3d4e5f6}. When you exploit a challenge or lab, the full flag is printed to the response body, written to a /flag.txt file, or returned in an API field — location depends on the bug. Flags are cryptographically generated at container startup from a secret, so they can't be guessed.
Submission is exact match. Copy the full string including the braces. No trailing whitespace. No partial credit.
BREACHFORGE_PORT=9090 (or any free port) and docker compose up -d again.compose up, then retry. If it persists, check docker compose logs php python node.BF{ through the closing } with no extra whitespace. The hash check is client-side — no server round-trip needed.docker compose down -v) to wipe volumes and rebuild. Note: this also clears your lab captures if they're stored server-side, but the default flag submission is localStorage only.