Authorization disclaimer. This walkthrough is limited to the intentionally vulnerable PortSwigger Web Security Academy lab linked below. Test only systems you own or are explicitly authorized to assess.
Overview
The lab’s public POST /forgot-password handler calls an internal route shaped as /api/internal/v1/users/{username}/field/email. Because the submitted username is concatenated into that path, dot segments can change the route and %23 can discard the suffix appended by the gateway.

Lab objective/setup
Use POST /forgot-password to test how username is concatenated into the internal REST path. The objective is to traverse from the appended user segment to the v1 passwordResetToken field, reset the administrator password, and delete carlos.
What I observed
Query-string separators such as & and ? produced Invalid Route, indicating that the whole value was being treated as a path component rather than parsed as another query parameter. By contrast, ./administrator retained the normal response, ../administrator changed route matching, and excessive traversal produced a 404 that referenced the API definition. Together, those responses showed that the backend normalized REST path segments.





Why it is vulnerable
Concatenating untrusted input into an internal path allows the caller to change routing semantics after the public endpoint has accepted the request.
Walkthrough in the authorized PortSwigger lab
- Submit the forgot-password request and send it to Repeater.
- Compare
username=./administrator,username=../administrator, andusername=../../../../. The different responses confirm path normalization and provide the API-definition clue. - Request
GET /api/openapi.json. The schema documents/api/internal/v1/users/{username}/field/{field}and identifiespasswordResetTokenas an available field. - Submit
username=../../v1/users/administrator/field/passwordResetToken%23. After normalization, the internal request targets the administrator token while the decoded#prevents the automatically appended/field/emailsuffix from affecting the route. - Use the returned token in the lab’s reset-password flow, sign in as
administrator, and deletecarlosto meet the Academy objective.
1 | username=./administrator → normal response ("If that account exists…") |
1 | GET /api/openapi.json |
1 | username=../../v1/users/administrator/field/passwordResetToken%23 |










Verification
The administrator reset token is returned by the normalized v1 field path. Using it to reset the password and delete carlos changes the Academy state to solved.

Impact and defensive lesson
The lab demonstrates account takeover when untrusted input is concatenated into an internal REST path. Build backend requests with fixed route templates and validated path parameters, reject path separators and dot segments where usernames are expected, avoid relying on fragment behavior to sanitize a constructed URL, and restrict sensitive internal fields independently of gateway routing.