| Field | Detail |
|---|---|
| Platform | PortSwigger Web Security Academy |
| Type | Authentication — Broken Brute-Force Protection, Array-Based Bypass |
| Difficulty | Expert |
| Objective | Brute-force Carlos's password, then access his account page |
| Note | Victim username carlos |
Broken Brute-Force Protection, Multiple Credentials per Request¶
Trying to log in as carlos:teto revealed that the login endpoint accepts JSON:
POST /login HTTP/2
Host: 0a1a001d035ed8c480d7cc3000d30003.web-security-academy.net
Cookie: session=SufzEI9hlUo3VeSxn1BJbatcKYDE2gHc
{"username":"carlos","password":"teto"}
Since password is a JSON value rather than a flat form field, it doesn't have to stay a single string — JSON allows arrays. If the backend iterates the array and accepts a match against any element, every candidate gets tried inside a single HTTP request. Brute-force protection that counts failed login attempts is completely blind to this: the entire wordlist arrives as one request, so there's no volume to rate-limit or lock out against.
I built a JSON array from the candidate password wordlist:
❯ cat passwords | while read line; do echo "\"$line\","; done > password2
producing a password field like:
"username":"carlos","password":["123456",
"password",
"12345678"
(...)]
Sending the request with the full array returned a 302 Found — the server found a match somewhere inside it and succeeded the whole request. The response carried a valid session cookie for Carlos. I added it to the browser:
Clicking "My account" confirmed the session was valid. This isn't brute-forcing one password at a time — it's exploiting how the backend processes the field when given an array instead of a scalar. JSON-based login endpoints deserve a structural check beyond "what's a valid value here": testing whether a normally-scalar field accepts an array, object, or other JSON type can surface this kind of logic flaw before any traditional brute-force approach even gets started.
Lab solved :P