Introduction
Password recovery mechanisms are one of the most crucial security components in any modern web application. A secure “Forgot Password” process ensures that users can recover access to their accounts without exposing themselves to unnecessary risks.
OWASP’s Forgot Password Cheat Sheet does a great job of outlining best practices for building safe and resilient recovery flows.
However, during a recent security assessment, we discovered that even when some of these measures were seemingly in place, an attacker could still bypass the protections and execute a successful brute-force attack against the password recovery codes.
Vulnerability Overview
At first glance, the application appeared to implement stronger protections than typically encountered during security assessments. The recovery process relied on several mechanisms designed to secure the flow:
• Each password recovery flow generated a unique recovery_flow_token,
• Requests required a valid CSRF token,
• Session cookies tied each flow to a specific browser session.
It took us some time to identify the vulnerability, as the existing protections initially appeared difficult to bypass. At first glance, the recovery process looked solid: each request relied on a unique recovery_flow_token, and every submission required a valid CSRF token.
To automate the attack efficiently, we initially used Selenium. It simply simulated user interactions by opening the recovery page, entering the email address, “clicking” through the process, and capturing the tokens directly from the network logs. In reality, though, Selenium was not essential. Since it executes the same JavaScript as any standard browser, the entire process could just as well be replicated without it by analyzing the frontend logic and adjusting the exploit code.
That said, this approach is not guaranteed to work in every environment. Some applications heavily obfuscate their JavaScript or generate tokens using complex client-side computations, browser fingerprinting, or other mechanisms. In such cases, directly simulating the API calls might be a nightmare, and using Selenium or a headless browser would remain the more practical choice.
How the attack works:
1. Start a recovery flow
The attacker requests a password reset and receives a new recovery_flow_token and CSRF token.
2. Capture tokens and session data
Selenium is used to open the recovery page, submit an email address, and extract the tokens from network requests.
3. Send multiple code guesses
For each recovery flow, three random 6-digit codes are submitted.
4. Restart and repeat
After three attempts, a new flow is generated, and the process loops indefinitely.
Because there were no global rate limits, account lockouts, or CAPTCHA challenges, this approach made brute-forcing recovery codes both practical and efficient.
Proof of Concept (PoC)
Below is the original PoC script we used to demonstrate the vulnerability.
We anonymized sensitive details but kept the core logic intact.

This PoC demonstrates that by constantly restarting recovery flows, an attacker can completely bypass per-session attempt limits and brute-force codes until the correct one is found
Impact
This vulnerability makes full account takeover possible if the attacker knows the victim’s email address.
Because the recovery code space is limited, an attacker could make the exploit highly efficient and potentially succeed within a few hours or, at most, a few days.
Recommendations
To mitigate this vulnerability, we recommend:
• Global rate limiting - apply restrictions across all flows, users, and IPs.
• Stronger recovery tokens - replace 6-digit numeric codes with long random strings or UUIDs.
• CAPTCHA integration - introduce verification after multiple failed attempts.
• Anomaly detection - monitor for mass recovery requests and trigger alerts on suspicious patterns.
Conclusion
Even though the application used recovery_flow_token and CSRF tokens, the absence of global brute-force protections allowed attackers to bypass the password recovery security entirely.
Bruteforcing short recovery codes is feasible, cost-effective, and stealthy when no comprehensive safeguards are in place.
Implementing stronger tokens, global rate limits, and effective monitoring is essential to protect user accounts.