Skip to content

Fix Challenge 8 voting VERB-tampering flag leak (Challenge-81) - #494

Closed
beanbeah wants to merge 3 commits into
OWASP-CTF:dc34-ctffrom
beanbeah:ctf/r2-challenge-challenge-81-jwt-voting-challenge
Closed

beanbeah wants to merge 3 commits into
OWASP-CTF:dc34-ctffrom
beanbeah:ctf/r2-challenge-challenge-81-jwt-voting-challenge

Conversation

@beanbeah

@beanbeah beanbeah commented Aug 9, 2026

Copy link
Copy Markdown

Vulnerability

Assignment8.vote() (Challenge 8 – star rating / voting) tried to gate the privileged branch (record a vote and return the flag in an X-FlagController response header) with a denylist:

if (request.getMethod().equals("GET")) {
  // return "you need to login" message
}
// otherwise: record vote + leak flag

The handler is mapped with @GetMapping, so Spring MVC rejects POST/PUT/etc. with a 405 before the method body ever runs — but HEAD is implicitly accepted on a @GetMapping route (HTTP defines HEAD as "GET without a body"), so request.getMethod() returns "HEAD", which is never equal to the literal string "GET". That let an attacker reach the privileged branch with a single HEAD request and read the flag straight out of the response header, without any real authentication check ever being performed.

Fix

Replaced the denylist with a fail-closed default: since this endpoint has no real login/session check behind it at all, no HTTP verb should reach the privileged branch. Every request now gets the same "you need to login first" response that GET already returned, regardless of method.

Verification (local, WSL)

Built and ran the app locally (java -cp target/classes:<deps> org.owasp.webgoat.server.StartWebGoat), registered a test user, and hit /WebGoat/challenge/8/vote/3 directly:

  • Before the fix: curl -X HEAD .../challenge/8/vote/3200 with an X-FlagController header containing the flag.
  • After the fix:
    • GET (the only method the real UI, challenge8.js, ever sends) → unchanged 200 JSON {"error":true,"message":"Sorry but you need to login first in order to vote"}.
    • HEAD (verb-tampering exploit) → 200, empty body, no X-FlagController header.
    • POST/PUT → unaffected 405 Method Not Allowed (already enforced by @GetMapping, unrelated to this fix).

Legitimate feature (the rating widget on the Challenge 8 page) is unaffected since it only ever issues GET requests, whose response is byte-for-byte identical to before.

🤖 Generated with Claude Code

Assignment8.vote() denylisted only the literal string "GET" via
request.getMethod().equals("GET") and let every other HTTP verb fall
through to the privileged branch that records a vote and returns the
flag in an X-FlagController response header. Since the WebGoat UI only
ever calls this endpoint with GET (challenge8.js issues $.get(...)),
any other verb - POST, PUT, HEAD, etc. - reached the "authenticated"
branch despite there being no actual authentication check at all,
letting an attacker who simply changes the request method obtain the
flag.

Replace the denylist with a fail-closed default: since there is no
real login/session check backing this endpoint, no verb should reach
the privileged branch, so every request (regardless of method) now
gets the same not-logged-in response that GET already returned. The
legitimate feature is unaffected because the only real caller
(challenge8.js) always issues GET requests, which behave identically
to before.

Closes Challenge-81-JWT-Voting-Challenge (A01 Broken Access Control).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Aug 9, 2026

Copy link
Copy Markdown

🏆 WebGoat — CTF Patch Score

░░░░░░░░░░░░░░░░░░░░  3 / 137 pts  (2%)

1 / 69 challenges patched

Per-challenge detail is withheld — it would reveal the rubric.

Commit: 211c6bd · scoring run

🎉 Your result is on the leaderboard — see where you rank! 🏆

beanbeah and others added 2 commits August 9, 2026 11:41
The CI scorer image (eclipse-temurin:25-jdk-noble) fails to compile
with lombok 1.18.36 pinned in pom.xml: 'Fatal error compiling:
java.lang.ExceptionInInitializerError: com.sun.tools.javac.code.TypeTag
:: UNKNOWN'. Lombok 1.18.36 predates JDK 25 compiler-internals support;
1.18.46 fixes it. Build-tooling-only change, no runtime/functional code
touched.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
… 500)

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@beanbeah

beanbeah commented Aug 9, 2026

Copy link
Copy Markdown
Author

Closing as part of a full stand-down of this CTF push.

@beanbeah beanbeah closed this Aug 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant