# Password reset token reuse and weak validation

Reset/confirmation tokens accepted after expiry, after single use, or with a tweaked character; parameter-source precedence on credential forms

_Area: Web authentication and account takeover. Seen in 2 disclosed reports; 2 confirmed bounties: $100 and $300; latest 2023-02-03. Updated 2026-11-01._

## How it works
A reset or confirmation token is meant to be a one-time, time-boxed proof that the holder controls the account. The assumption that breaks is that the server actually re-checks that proof at the moment it changes the secret or signs the user in. Instead the endpoint trusts something else that is still lying around: a weakly generated token that survives a character change, a server-side state blob (`__VIEWSTATE`) that outlives the link's expiry, or a request parameter whose source (GET vs POST) the server cannot tell apart. When the real token/expiry is not the thing being validated, the one-time property silently disappears.

## Where it shows up
- Finalization endpoints (e.g. `TokenValidation.aspx`, `/confirm_email`, `/confirm_device`) that accept a state field or session value instead of the token itself.
- Tokens with low entropy or a predictable structure — standalone digits, short numeric segments, sequential IDs.
- Expired-link pages that still render a usable form or hidden state.
- Credential forms (`change_password`, reset) that read field names like `newpassword` from more than one source.
- Confirmation links leaked in URLs, referrers, or indexed pages.

## How to review
Identify the exact value the finalization step authorizes on, then ask: is that value the token, and is expiry and single-use re-checked server-side at that call? The invariant: **the password change or login must be gated on a token that is verified for authenticity, not expired, and not previously consumed — checked at the finalization request, not at page render.** For credential forms, confirm the server binds each field to one source (POST body) and rejects or ignores same-named query parameters. For tokens, confirm sufficient randomness and that a mutated character fails.

## How finders spotted it
- Noticed the change_password endpoint accepted `newpassword`/`newpassword2` as query params and that they took precedence over POST values [E1](https://bugbountylibrary.com/r/h1%3A96636.md).
- Google-dorked for leaked `inurl:token` links, saw an expired email link still carried a token, and hypothesized weak tokens could be reused by tweaking a character — specifically a digit not embedded between letters [E2](https://bugbountylibrary.com/r/h1%3A1817214.md).
- Saw the expired-link page still rendered a form with `__VIEWSTATE`, hypothesized the server trusted that state rather than the token, and replayed it to the finalization endpoint [E3](https://bugbountylibrary.com/r/h1%3A1615790.md).

## Variants seen
- Parameter-source precedence: GET overrides POST on a password form, fixing the victim's new password to a known value [E1](https://bugbountylibrary.com/r/h1%3A96636.md).
- Weak token generation: single-character mutation of an already-used confirmation token yields a valid one that logs the attacker in [E2](https://bugbountylibrary.com/r/h1%3A1817214.md).
- Expiry not re-validated: server trusts `__VIEWSTATE` so expired reset links work indefinitely [E3](https://bugbountylibrary.com/r/h1%3A1615790.md).

## Impact and severity
All three led to account takeover or attacker-controlled credentials [E1](https://bugbountylibrary.com/r/h1%3A96636.md)[E2](https://bugbountylibrary.com/r/h1%3A1817214.md)[E3](https://bugbountylibrary.com/r/h1%3A1615790.md). Severity rises when the endpoint signs the user in without a password [E2](https://bugbountylibrary.com/r/h1%3A1817214.md) and when the same weakness spans multiple token types (`/confirm_email` and `/confirm_device`), reaching profile settings [E2](https://bugbountylibrary.com/r/h1%3A1817214.md). Bounties were modest (median 300) but impact was full account compromise.

## Evidence
- E1: https://bugbountylibrary.com/r/h1%3A96636.md
- E2: https://bugbountylibrary.com/r/h1%3A1817214.md
- E3: https://bugbountylibrary.com/r/h1%3A1615790.md
