October 27, 2026
WebVerse Pro Labs β Web Foundations: A Complete Walkthrough
Ten bite-sized web challenges that teach the habits every bug hunter leans on before they ever touch a scanner.
By saurav singh
11 min read
Introduction
The Web Foundations track on WebVerse Pro Labs is deceptively simple. There's no SQL injection, no deserialization gadget chain, no race condition. Every single flag in this track is sitting in plain sight β in a header, a cookie, a comment, a forgotten file. The whole point is to build the reflex of looking properly before you reach for heavier tooling.
Toolkit
Nothing exotic is required:
- Browser DevTools β the Network, Application (cookies/storage), and Sources tabs
- Burp Suite (Community is fine) β Proxy + Repeater, for everything DevTools can't fully capture
curlβ fast header and response inspection from the terminalgit-dumperβ reconstructing a leaked.git/directoryffuf+ a wordlist (e.g. SecListscommon.txt) β content discovery
1. Header Hunt
Scenario - Arc Logistics, a regional freight carrier, rushed a public shipment-tracking site to hit a Q2 deadline and merged a debugging branch the night before launch. Something got left behind.
Walkthrough-
- Load the site and open DevTools β Network tab (or just
curl -I <url>). - Inspect the full set of response headers rather than the rendered page.
Root cause - Debug/diagnostic headers (X-Debug-*, X-Flag, custom build markers) were emitted by a branch that was never meant to ship. Headers are invisible in the rendered page, so nobody "saw" the leak during QA.
Takeaway - Headers are the first thing to read on any target, and the last thing most people check. Always diff the headers against what a boring, correctly configured server would return.
2. Cookie Cutter
Scenario- Northbrew Coffee Co. launched a loyalty-rewards site and baked the entire account state into one browser cookie "so the experience would feel instant." They picked an encoding that's easy to read on the wire β and easier still to read off it.
Walkthrough-
- Headers and source β nothing. Move to DevTools β Application β Cookies.
- A cookie named
nb_sessionholds a long string ending in == β the classic tell of Base64.
nb_session = eyJ0aWVyIjogImdvbGQiLCAiYmVhbl9iYWxhbmNlIjog...nb_session = eyJ0aWVyIjogImdvbGQiLCAiYmVhbl9iYWxhbmNlIjog...
- Decode it:
echo 'eyJ0aWVyIjog...==' | base64 -decho 'eyJ0aWVyIjog...==' | base64 -d- The decoded JSON carries the account state β and a
debugobject the developers forgot to strip:
{
"tier": "gold",
"bean_balance": 247,
"next_perk": "free_oat_milk",
"joined": "2025-08-14",
"debug": {
"feature_flag": "rewards_v2_live",
"internal_ref": "WEBVERSE{β¦redactedβ¦}"
}
}{
"tier": "gold",
"bean_balance": 247,
"next_perk": "free_oat_milk",
"joined": "2025-08-14",
"debug": {
"feature_flag": "rewards_v2_live",
"internal_ref": "WEBVERSE{β¦redactedβ¦}"
}
}Root cause - Base64 is encoding, not encryption. It provides zero confidentiality β anyone who sees the cookie can decode it instantly. Stuffing internal debug references into client-held state compounds the problem.
Takeaway - Any cookie/token value that's long and ends in = or == should be decoded on sight. If sensitive state lives client-side, it must be signed (to prevent tampering) and anything confidential must be encrypted β ideally it shouldn't live client-side at all.
3. Session Swap
Scenario- Ridgeline Hotels shipped an internal portal for front-desk agents, housekeeping leads and assistant managers. Role-based access control lives entirely in a cookie the browser is happy to edit.
Walkthrough-
- Headers and source β nothing. Check DevTools β Application β Cookies.
- A cookie named
rolewith the valueuser.
- Edit the cookie value in place (DevTools, or Burp on the request) to a privileged role β
adminis the obvious first guess.
Cookie: role=adminCookie: role=admin
- Reload. The portal now serves the privileged view, and the flag along with it.
Root cause - Authorization decisions were trusted to a value the client fully controls. The server never validated the role server-side against an authenticated session β it just read the cookie and believed it.
Takeaway - Never trust the client for authorization. Roles and entitlements must be derived server-side from an authenticated, tamper-proof session. If you must store role state client-side, it has to be cryptographically signed and verified on every request. When you see a plaintext role, isAdmin, is_staff or level cookie, test it immediately.
4. Redirect Run
Scenario - Quikpay generates short, shareable receipt URLs. Visiting one bounces you to a friendly "thanks for your purchase" landing page. The redirect isn't as silent as it looks β something didn't get stripped from the intermediate response.
Walkthrough-
- Visiting the link follows a 302 straight to the landing page, so the browser never shows you the intermediate response.
- DevTools' Network tab couldn't capture the 302 body completely β the redirect is followed too fast and the intermediate body is discarded.
- Intercept with Burp Proxy instead. Catch the 302 response before the browser follows it, and read its body in the Repeater/intercept view. The flag is in the body of the redirect response itself.
Key detail from the field: the browser DevTools silently drop the body of a redirect it auto-follows. Burp (or
curlwithout-L) is the only reliable way to read what a 3xx actually returned.
# -i shows headers + body; no -L means we do NOT follow the redirect
curl -si https://<target>/receipt/<id># -i shows headers + body; no -L means we do NOT follow the redirect
curl -si https://<target>/receipt/<id>Root cause - Developers assume a redirect response has no meaningful body because browsers don't render it. So debug content, tokens, or internal data get left in the body of a 301/302, invisible in normal use.
Takeaway - A redirect is still a full HTTP response with headers and a body. Always inspect 3xx responses directly β turn off "follow redirects," intercept in Burp, or use curl -i without -L.
5. Vellichor Press
Scenario - Vellichor Press is a six-person literary magazine. Their engineer promised "one more pass after launch." It never happened. The masthead looks immaculate β but a draft admin note sits where nobody scrolls.
Walkthrough -
- View the page source (
Ctrl+U) rather than the rendered page. - Search the markup for
admin, and for HTML comments ().
- A developer comment near the admin endpoint contains the flag β invisible to a reader, plain as day in the source.
curl -s https://<target>/ | grep -nE '<!--|admin'curl -s https://<target>/ | grep -nE '<!--|admin'Root cause - Comments are stripped from the rendered DOM but shipped verbatim to every visitor in the HTML source. Developers leave notes, endpoint hints, credentials and TODOs in comments assuming nobody reads source.
Takeaway - Always read the raw source, not just the rendered page. Grep for <!--, TODO, FIXME, admin, password, key, internal. This extends to the JS bundles the page pulls in (see Pebble & Pine).
6. Sundial Observatory
Scenario- An amateur astronomy club's site is run out of a retiree's garage. He believes β on principle β that politely asking search engines to stay away is the same as keeping a page private.
Walkthrough -
- Headers and source β nothing. Request
/robots.txtdirectly (the briefing all but spells it out).
# Sundial Observatory β please be courteous, search engines.
User-agent: *
Disallow: /members-only-2026
Disallow: /staff-archive
Disallow: /old-roster.html
Sitemap: /sitemap.xml# Sundial Observatory β please be courteous, search engines.
User-agent: *
Disallow: /members-only-2026
Disallow: /staff-archive
Disallow: /old-roster.html
Sitemap: /sitemap.xml- Each
Disallowline is a signpost to a path the owner wants hidden. Visit them./members-only-2026holds the flag.
Root cause - robots.txt is a request to well-behaved crawlers, not an access control. Worse, it's a public file that advertises exactly the paths the owner considers sensitive β the opposite of hiding them.
Takeaway - robots.txt is free reconnaissance. Read it on every target, and treat every Disallow entry as a lead. Don't miss the Sitemap: directive β sitemap.xml often enumerates even more paths. (Obscurity is not a control; those paths need real authentication.)
7. Pebble & Pine
Scenario - A small ceramics studio's site was built by the owner with help from a brother who "knows just enough JavaScript to be dangerous." The analytics file is his.
Walkthrough -
- View source and enumerate the scripts the page loads. One stands out:
/static/js/analytics.js. - The briefing hints the analytics file was modified. Fetch and read the file directly.
curl -s https://<target>/static/js/analytics.js | lesscurl -s https://<target>/static/js/analytics.js | less- The flag is embedded in the JS β a stray constant, comment, or "debug" value the brother left in.
Root cause - Every .js file a page loads is fully readable by the client. Developers treat JS as "backend-ish" and hardcode secrets, API keys, internal endpoints and flags into it, forgetting it's shipped verbatim to the browser.
Takeaway - Enumerate and read every JavaScript file a target loads β first-party bundles especially. Grep them for key, token, secret, api, internal, endpoint paths and base64 blobs. Minified bundles are worth beautifying (DevTools' {} pretty-print) and skimming. This is one of the richest sources of real-world findings.
8. Loop & Roam Records
Scenario- An indie record label's site is maintained by a designer who was learning git when she built it. One night she committed a .env with prod credentials, noticed an hour later, ran git rm .env, and pushed. The deploy script rsyncs the entire working tree β including .git/ β to the public web root.
Walkthrough-
- The briefing points straight at it:
.git/is reachable on the web app. Confirm:
curl -s https://<target>/.git/HEAD
# ref: refs/heads/main β directory is exposedcurl -s https://<target>/.git/HEAD
# ref: refs/heads/main β directory is exposed
- Reconstruct the whole repository with git-dumper:
git-dumper https://<target>/.git/ ~/webverse/Web_Foundations/git-dumpgit-dumper https://<target>/.git/ ~/webverse/Web_Foundations/git-dump- The secret was deleted in a later commit, but git keeps history. Walk every commit on every branch, including diffs:
cd ~/webverse/Web_Foundations/git-dump
git log -p --allcd ~/webverse/Web_Foundations/git-dump
git log -p --all
- The removed
.envβ and the flag β appear in the diff of the commit that deleted it.
Root cause- git rm removes a file from the working tree and the next commit, but the file's contents remain in the repository's object history forever. Serving the .git/ directory publicly exposes that entire history to anyone. Deleting a secret in a new commit does not remove it from history.
Takeaway- Always probe for exposed VCS metadata (/.git/HEAD, /.git/config). If present, dump it and read the full history β git log -p --all, git show, and check all branches/stashes. And the defender's lesson: a leaked secret is compromised the moment it's pushed β the only remediation is to rotate the credential, not to delete the file.
9. Quikpay Receipts
Scenario- Quikpay is a small payments backend used by a few dozen indie software shops. They take the integration seriously and the design seriously. They also have a debug branch in the resend handler that the engineering lead added during a late-night incident and never wrapped in a feature flag.
Walkthrough-
- The briefing points at the resend handler, so start there β locate the resend endpoint (e.g.
/receipt/resend).
- A
GETreturns405 Method Not Allowed. That's not a dead end β it confirms the route exists but wants a different verb. Capture the request in Burp and replay it as aPOSTin Repeater.
- The
POSTcomes back as a JSON body. A JSON response to a form-shaped request is the tell: the handler is branching onContent-Type.
- Re-send the
POSTdeclaring JSON, with a body matching the handler's expected fields.
POST /receipt/resend HTTP/1.1
Host: <target>
Content-Type: application/json
Content-Length: NN
{"email":"<email-id>"}POST /receipt/resend HTTP/1.1
Host: <target>
Content-Type: application/json
Content-Length: NN
{"email":"<email-id>"}(Match the body to the handler's real schema; vary Content-Type between application/json and application/x-www-form-urlencoded to see which path you land on.)
Root cause- The handler selects its code path from the request's verb and Content-Type. The debug branch β left unguarded because it was never put behind a feature flag β emits internal diagnostic data (including the flag) when the request is shaped a particular way. The 405 exposed method-sensitivity; the JSON response exposed content-type-sensitivity.
Takeaway- Treat 405 Method Not Allowed as a signpost, not a wall: the endpoint is there and wants another verb. Once you're talking to it, vary the Content-Type and the body shape β debug and alternate code paths love to hide behind the less-common combination. Defenders: feature-flag or delete debug branches before they ship.
10. Brackish Brewing Co.
Scenario- Brackish Brewing's site is a Flask app with a small staff section. They moved off their old reverse proxy in early 2025 and nobody revisited the assumptions that section was quietly making about where its traffic comes from.
Walkthrough-
- Crawling and source review turn up nothing. Run content discovery with ffuf:
ffuf -c -w ~/Tools/wordlists/SecLists/Discovery/Web-Content/common.txt:FUZZ \
-u https://<target>/FUZZffuf -c -w ~/Tools/wordlists/SecLists/Discovery/Web-Content/common.txt:FUZZ \
-u https://<target>/FUZZThis surfaces /staff β 403 (internal use only), alongside robots.txt, visit, and .well-known/.
-
The
403is an IP-based gate: the app decides "internal" by reading a client-IP value. Because they swapped reverse proxies, the app most likely still trusts anX-Forwarded-Forheader that an external client can now set directly. -
Re-request
/staffwith a spoofed trusted/internal source:
GET /staff HTTP/1.1
Host: <target>
X-Forwarded-For: 127.0.0.1GET /staff HTTP/1.1
Host: <target>
X-Forwarded-For: 127.0.0.1
Try 127.0.0.1, then private ranges (10.0.0.1, 192.168.0.1). The staff page renders β flag included. (X-Real-IP is the fallback header to try if X-Forwarded-For is ignored.)
Root cause- The app derives the "client IP" for its access check from a request header instead of the real transport-layer peer. That's only safe when a trusted reverse proxy sets the header and the app refuses client-supplied values. After the proxy change, the app kept trusting a header the client fully controls β so the gate is trivially spoofable.
Takeaway- Any "internal only" or IP-gated resource is worth testing with forwarding headers: X-Forwarded-For, X-Real-IP, X-Forwarded-Host, X-Client-IP, X-Originating-IP. Defenders must take client IP only from the trusted proxy, strip inbound forwarding headers at the edge, and never treat an IP as authentication β put real auth on the staff section.
The checks these labs covered
- Response headers β read the full set on every request (
curl -I, DevTools Network). Hunt for customX-*, debug, and version headers. (Header Hunt) - Cookies β inspect every cookie in Application β Cookies. Long values ending in =/== are Base64; decode them. Plaintext
role/isAdmin/levelcookies are authorization you can edit. (Cookie Cutter, Session Swap) - HTML source & comments β read the raw source, not the render. Grep <!--,
TODO,FIXME,admin,password,key. (Vellichor Press) - JavaScript files β enumerate and read every loaded
.js; grep for secrets, endpoints, flags; pretty-print minified bundles. (Pebble & Pine) robots.txtβ everyDisallowis a lead; follow theSitemap:directive too. (Sundial Observatory)- Redirect (3xx) responses β a redirect has a body; browsers hide it. Intercept in Burp or use
curl -iwithout-L. (Redirect Run) - Exposed VCS directories β probe
/.git/HEAD; if present,git-dumper+git log -p --all. Secrets live in history even after deletion. (Loop & Roam) - Content discovery β when nothing's visible, brute-force paths:
ffuf -c -w common.txt -u https://target/FUZZ. (Brackish Brewing Co.) - HTTP method &
Content-Typeprobing β a405means the route exists but wants another verb; switching the verb and theContent-Typecan trip alternate/debug code paths. (Quikpay Receipts) - Forwarding / trust headers β
X-Forwarded-For/X-Real-IPto bypass IP-based "internal only" gates when a proxy's trust assumptions are stale. (Brackish Brewing Co.)
The meta-lesson
Every flag in this track existed because a developer trusted something they shouldn't have β the browser not to read a header, a user not to edit a cookie, a crawler to honor robots.txt, git rm to erase a secret. Your job as a tester is to systematically question each of those trust assumptions. The payload is rarely the hard part; noticing is.