Capture!
1. Reconnaissance
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.
2. Why Off-the-Shelf Tools Fail
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.
3. Username Enumeration
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.
4. CAPTCHA-Aware Password Brute Force
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 |
5. Flag
With valid credentials the login succeeds and the application returns the flag.
Key Takeaways
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.