Overview
I found a race condition in HackerOne’s two-factor authentication recovery flow. Racing the reset request created several independent recovery attempts. Cancelling one attempt did not cancel the others, so a user could believe the account was safe while another reset remained active.
The original report is available through HackerOne’s limited disclosure.
Expected recovery flow
With 2FA enabled, a normal sign-in requires a valid email and password followed by the authenticator code. If the code is unavailable, the sign-in page offers a recovery path.
Starting recovery displays a confirmation, then sends an email with a cancellation link. The reset completes after 24 hours unless the account owner cancels it.
After one request is active, the interface disables the reset link. That suggests recovery is a single active operation.
The race condition
The GraphQL mutation below creates a 2FA reset. Sending the same request concurrently produced multiple successful recovery attempts even though the interface presented the operation as single-use.
POST /graphql HTTP/2
Host: hackerone.com
Content-Type: application/json
X-Csrf-Token: <CSRF_TOKEN>
{
"operationName": "TwoFactorReset",
"variables": {
"email": "<EMAIL>",
"password": "<PASSWORD>",
"fingerprint": "<FINGERPRINT>"
}
}
Impact and prerequisites
An attacker first needed the account’s valid email and password. They could then create several recovery attempts. If the account owner clicked one cancellation link, the remaining attempts stayed active. Exploitation also depended on the owner not noticing and cancelling every request before the recovery window ended.
Resolution
HackerOne rated the report Medium. The production fix made cancellation apply to the full recovery set: using any cancellation link invalidates the other reset and cancellation links, and the user is notified.
Takeaway
A recovery flow is a state machine, not just a button. Rate limits and disabled UI controls are not enough when concurrent requests can create several valid states. Creating or cancelling one recovery attempt should invalidate every competing attempt for that account.