demo ctf 2026 — baby steps

event: Demo CTF 2026 · web · easy · 100 pts

Training fixture. Demo CTF 2026 is an invented event I host against myself. The challenge, the box, and the flag are mine; this writeup exists to keep the format — including the failed attempts — honest.

Description

baby steps — web, 100 pts

“Our new site is so secure even the crawlers behave.” http://demo-ctf.local:9000/

A single static-looking page reading “nothing to see here.” No files attached, no parameters, no forms. Easy tier: the challenge is about looking where the site tells you not to look.

Thought process

Failed attempt 1 — brute force before reading

I pointed feroxbuster at the root with a common wordlist before doing any manual enumeration. Ten minutes, a few thousand requests, one hit: /robots.txt — which I had not read yet. Classic own goal: the scanner found the map and I ignored it.

$ feroxbuster -u http://demo-ctf.local:9000/ -w common.txt -q
200      GET       21l       45w      402c http://demo-ctf.local:9000/robots.txt
# ... nothing else

Failed attempt 2 — taking the page at face value

After finally reading robots.txt (below) I fetched the disallowed path, saw a page that said “flag is not here”, and briefly went back to fuzzing for other paths. Cost: another few minutes. The page was lying by omission — I had read the rendered text but not the source. On a static challenge, view-source is not optional.

Solution

Two requests. First, the map the site hands you for free:

$ curl -s http://demo-ctf.local:9000/robots.txt
User-agent: *
Disallow: /s3cr3t-adm1n/

Disallow is an instruction to polite crawlers, not an access control — the path serves 200 to anyone who asks. Asking, and reading the source this time:

$ curl -s http://demo-ctf.local:9000/s3cr3t-adm1n/ | grep -io 'flag{[^}]*}'
flag{robots_txt_is_a_map_not_a_fence}

The rendered page says “flag is not here”; the HTML source carries the flag in a comment immediately under it.

Flag

Lessons

  • Read robots.txt (and sitemap.xml, and .well-known/) before reaching for a wordlist. The scanner is a supplement, not a substitute.
  • Rendered text is a suggestion; source is the truth. Grep every response for the flag format early: grep -io 'flag{[^}]*}' costs nothing.
  • For defenders: Disallow does not protect anything. If a path must not be public, authenticate it — or better, don’t deploy it.
  • Next practice: same shape, harder tier — a disallowed path behind a referer/header check, to drill request malleation instead of plain curl.