Skip to content
Hasaan
Technical writing

Zero-Interaction Account Takeover Through Email-Change Workflow Failures

A first-hand breakdown of how identifier enumeration, weak request binding, and unrestricted confirmation-code attempts combined into account takeover without victim interaction.

By Hasaan Muhammed6 min read
  • Account takeover
  • Business logic
  • Rate limiting
  • Penetration testing

This investigation began with an email-change function and ended with an account takeover that required no action from the victim. The outcome depended on several weaknesses operating together: enumerable user identifiers, disclosure of the email associated with each identifier, insufficient authorization around the change request, and unrestricted attempts against a six-digit confirmation code.

All hostnames, identifiers, addresses, tokens, and request values below are synthetic. The original finding was made during an authorized engagement.

Understanding the workflow

The application accepted an email-change request containing the following logical inputs:

  • a five-digit user identifier;
  • the account's current email address;
  • the proposed new email address; and
  • a CSRF token.

The presence of a CSRF parameter initially suggested that the state-changing operation had request-level protection. Testing showed that the server did not meaningfully validate that value for this workflow, and the request was not strongly bound to the authenticated account.

A simplified request looked like this:

POST /users/change-email HTTP/1.1
Host: app.example.com
Content-Type: application/x-www-form-urlencoded

_method=PUT&_csrfToken=REDACTED&id=00000&current_email=victim@example.com&new_email=researcher@example.net&email_again=researcher@example.net

User enumeration

The application did not expose a normal directory of user IDs or addresses. However, responses from the email-change request varied when a valid five-digit identifier was supplied. Iterating through the identifier space revealed valid IDs and the email associated with each account.

The security impact was greater than a conventional enumeration issue because the disclosed values were exactly the inputs needed by the next stage of the email-change workflow.

Confirmation-code weakness

Submitting a valid ID, the matching current address, and a new address caused the application to send a six-digit confirmation number to the account's existing email address. Sending that notification was not itself a bypass; the victim still possessed the code.

The validation endpoint, however, did not impose an effective attempt limit. The six-digit value could be tested repeatedly until the correct confirmation number was found. Once accepted, the application replaced the victim's email with the researcher-controlled address, enabling the normal account-recovery path to complete the takeover.

The end-to-end sequence was:

  1. Intercept an authorized email-change request.
  2. Vary the five-digit ID and identify response differences.
  3. Recover a valid user ID and its associated email address.
  4. Submit an email-change request for that account.
  5. Repeatedly test confirmation numbers against an endpoint without effective throttling or transaction lockout.
  6. Confirm the new address and use the application's normal recovery process.

No click or approval from the victim was required. “Zero interaction” is a description of victim involvement, not of attacker effort: exploitation still required enumeration, request construction, and confirmation-code testing.

Root causes

  • Broken object-level authorization: the server trusted a user ID supplied by the request instead of deriving the target account from the authenticated session and re-authorizing the change.
  • Information disclosure: response differences exposed valid identifiers and associated email addresses.
  • Ineffective CSRF validation: a token-shaped parameter was present but did not provide meaningful request integrity.
  • Missing rate limits: a six-digit security control was exposed to unrestricted online guessing.

Defensive engineering lessons

  • Derive the account being changed from the authenticated session. Never trust a client-supplied user ID for a sensitive profile operation.
  • Require recent reauthentication before changing an email address, password, MFA method, or recovery channel.
  • Validate CSRF protection server-side and bind it to the authenticated session and state-changing action.
  • Generate confirmation codes with a short lifetime, single-use semantics, and binding to the exact account and pending address change.
  • Apply per-account, per-transaction, per-IP, and global abuse controls. Lock or invalidate a transaction after a small number of failed attempts.
  • Return consistent responses for valid and invalid account identifiers and monitor enumeration patterns.
  • Notify both the old and new addresses and provide a safe recovery path, but do not treat notification as a substitute for authorization.

Why the chain mattered

Individually, each issue might have received a lower severity: identifier enumeration, a weak CSRF check, or missing code throttling. Together they removed every control protecting the email-change process. Testing business logic as a complete state machine—rather than assessing each request in isolation—was what exposed the account-takeover path.

Further reading