Difficulty: 2.5/5 (field-calibrated) · Docker Compose · 6 services
A multi-surface web drill: five flags spread across five deliberate trigger points (SQLi, reflected XSS, stored XSS × admin bot, command injection, custom-protocol metrics) plus an asset-hygiene playground. The portal tells you where everything is — earning the flags is the other part.
Drill Corp sells drill bits and safety goggles through a portal that ops built in a weekend. It self-declares every endpoint it has, runs a guestbook, a "report a broken page" queue that an admin bot patrols, a ping tool "for diagnostics", a JSON product API, an intranet metrics agent nobody documented, and a frozen legacy marketing site nobody decommissioned. Security assumes nobody looks at all of it at once. You look at all of it at once.
./RESET.sh # first run: seeds .env, builds, starts, health-gates
# once .env exists, plain compose also works:
docker compose up -d --build| host port | service | hostname alias | what lives there |
|---|---|---|---|
| 18101 | app |
www.drill.corp |
portal, /search, /guestbook, /report, /tools/ping, /admin/* |
| 18102 | api |
api.drill.corp |
JSON product API |
| 18103 | metrics |
intranet.drill.corp |
custom-protocol metrics agent (no docs) |
| 18104 | legacy |
legacy.drill.corp |
frozen Apache marketing site |
| 16380 | redis |
— | cache backend, no auth |
The admin bot (bot/) is internal — it publishes no port.
Add the aliases to your attacking machine's /etc/hosts (replace
<lab-host> with the machine running the lab):
<lab-host> www.drill.corp api.drill.corp intranet.drill.corp legacy.drill.corp
Requirements: Docker with Compose v2. Flag values and the admin token
are seeded into .env by RESET.sh (defaults match SOLUTION.md);
set your own values there if you prefer.
| path | role |
|---|---|
app/ |
main web app — reflected XSS, stored XSS, cmdi, admin gate, cmdi flag file |
api/ |
product API — SQLi trigger point over SQLite |
metrics/ |
custom-protocol intranet metrics — flag rides in a metric label |
legacy/ |
static Apache site — .env leak + open server-status (asset hygiene, no flag) |
bot/ |
headless-Chromium admin bot that patrols reported URLs |
secrets/ |
the admin-secret flag file (bind-mounted read-only) |
CHALLENGE.md |
black-box brief — read this first, zero spoilers |
SOLUTION.md |
full walkthrough (flags included) — after you give up or win |
DIFFICULTY.md |
nominal vs field-calibrated difficulty |
RESET.sh |
deterministic teardown / rebuild / health-gate |
Five flags live in this stack — one per trigger surface:
- a row in the product API's SQLite database,
- a line on the admin panel ("release note of the day"),
- a file behind the admin secret endpoint,
- a file in the app container's
/opt, - a label value inside a custom-protocol metrics scrape.
Values are in SOLUTION.md (and in the seeded .env / flag files).
Some flags are visible to anonymous users; the interesting ones are
behind a session only the admin bot holds.
./RESET.sh — tears down volumes, rebuilds, re-seeds .env if missing,
and gates on the portal answering on 18101 (RESET-OK /
RESET-DEGRADED). Guestbook/report state is in-memory, so every reset
is clean. The target is fully reproducible from this repo.
MIT. Part of the <target>-lab pentest series — targets built by a human
forge-master and field-calibrated by an AI pentest agent (first-blood and
timing data in DIFFICULTY.md).