The target presents a single login form. Two fields request a username and password, but a third field demands the answer to a randomly generated arithmetic expression — a math-based CAPTCHA — on every request. The CAPTCHA value rotates with each page load, which is precisely the control intended to defeat automated attacks.
Submitting a few requests by hand surfaces the first real weakness. The application returns different error messages depending on whether the username exists. A non-existent account and an existing account with a wrong password produce distinguishable responses. This is a classic username enumeration vulnerability.
The instinct is to point hydra at the form. It does not work. Hydra and similar tools submit static parameter sets — they cannot read, parse, and solve the rotating CAPTCHA expression embedded in the page on each attempt. The adaptive CAPTCHA invalidates any pre-baked request.
The form must be defeated by a client that behaves like a browser: fetch the page, extract the live CAPTCHA, compute its answer, and submit all three fields together in a single coherent request.
Using the distinguishable error responses, a wordlist of candidate usernames is fed through a Python loop. For each candidate the script reads the response body and checks which error class was returned, confirming valid accounts before any password guessing begins.
A critical gotcha: the response text contained HTML-encoded apostrophes ('). Matching error strings against the rendered text silently failed until the comparison was done against the raw HTTP body. Always inspect raw HTTP, never rendered text.
Once a valid username is confirmed, the same machinery drives the password attack. Each iteration fetches a fresh page, solves the new CAPTCHA expression programmatically, and submits the guess. Because the script regenerates the CAPTCHA answer every request, the rate-limiting control is rendered ineffective.
| Step | Action | Outcome |
|---|---|---|
| Enumerate | Loop usernames, classify error messages | Valid account identified |
| Parse | lxml extracts the live CAPTCHA expression | Arithmetic answer computed |
| Submit | POST username + password + CAPTCHA together | One valid login attempt per request |
| Iterate | Repeat across password wordlist | Correct credentials recovered |
With valid credentials the login succeeds and the application returns the flag.
Differential error messages reveal whether a username exists before the password attempt. CAPTCHAs that appear in the HTML response body can be solved programmatically — always inspect raw HTTP, not rendered output.
Use identical error messages regardless of whether username or password is wrong. Implement server-side CAPTCHA validation with a CAPTCHA that cannot be solved by parsing the response body. Add rate limiting and account lockout.