October 10, 2026
$9,600 for Trusting a Header the Client Writes: Password Reset Poisoning via Host Header Injection
The Host header exists so a single server can host multiple domains and know which one a given request is actually for. It's alsoβ¦

By T4nv1
4 min read
The Host header exists so a single server can host multiple domains and know which one a given request is actually for. It's also, structurally, just another header the client sends β fully attacker-controlled, in the same way any other request header is, even though applications routinely treat it with a level of implicit trust closer to how they'd treat something the server itself generated.
The target, which I'll call Hazelbrook here per their program's anonymization terms, is a subscription management platform with a standard "forgot password" flow β submit your email, receive a link, click it, set a new password.
Why the Reset Link's Domain Matters More Than It Looks
I intercepted a password reset request and looked at the resulting email's link structure, then went back and checked how the server had actually built that link β specifically, whether the domain portion came from a fixed, server-side configuration value, or was constructed dynamically using the incoming request's own Host header, which is a common shortcut for applications that need to support multiple environments (staging, regional domains, custom customer subdomains) without hardcoding a single domain into every generated link.
I submitted a password reset request with a deliberately altered Host header:
POST /api/auth/forgot-password HTTP/1.1
Host: attacker-controlled.com
Content-Type: application/json
{ "email": "myaccount@hazelbrook-test.com" }POST /api/auth/forgot-password HTTP/1.1
Host: attacker-controlled.com
Content-Type: application/json
{ "email": "myaccount@hazelbrook-test.com" }The reset email that arrived at my test inbox a moment later contained:
https://attacker-controlled.com/reset-password?token=8f2e91a3b7c44d...https://attacker-controlled.com/reset-password?token=8f2e91a3b7c44d...Confirmed: the server built the reset link's domain directly from the request's Host header, with no validation that the value matched any legitimate, expected domain for the application at all.
Turning This Into an Account Takeover Primitive
A reset link pointing at an attacker's domain is only dangerous if the attacker can get the victim to actually trigger a reset and then intercept the resulting token β which requires either the victim submitting the forgot-password form themselves after visiting a crafted page, or, more directly, the attacker triggering the reset on the victim's behalf and capturing the resulting email before the victim notices it.
Password reset requests typically don't require authentication, since the entire point is recovering access without being logged in β meaning anyone can trigger a reset for any known email address. If I, as an attacker, know a victim's email and can get my Host header value into a request Hazelbrook's server treats as legitimate, I can trigger a reset using the victim's email while setting the Host header to a domain I control:
POST /api/auth/forgot-password HTTP/1.1
Host: attacker-controlled.com
{ "email": "victim@realcompany.com" }POST /api/auth/forgot-password HTTP/1.1
Host: attacker-controlled.com
{ "email": "victim@realcompany.com" }This sends a password reset email to the real victim's inbox β I never see that email, and have no access to it β but the link inside it points at attacker-controlled.com rather than Hazelbrook's real domain. If the victim, trusting the email came from Hazelbrook and not scrutinizing the link's domain closely (a very reasonable assumption for most users, especially since the rest of the email's content was entirely legitimate, generated by Hazelbrook's own template), clicks the link, their browser sends the valid reset token to my server instead of Hazelbrook's. I'd then have a real, valid reset token for the victim's account, usable by replaying it against Hazelbrook's actual password-reset endpoint directly.
Confirming End-to-End, With My Own Email Only
I tested this complete chain using two email addresses I controlled, one standing in as "victim," one as the destination for the poisoned link's capture. I triggered the reset with the Host header pointed at infrastructure I owned, received the token at my own "victim" inbox exactly as a real user would, manually visited the poisoned link, confirmed my own simple logging page captured the token, and then submitted that captured token directly against Hazelbrook's real reset-password endpoint β successfully resetting that test account's password using a token that had, by design, briefly passed through infrastructure I controlled.
Scope
Both the "victim" and "capture" roles in this chain were email addresses and accounts fully under my own control. I never sent a reset request for any real third-party account, and the poisoned link was never distributed anywhere beyond my own controlled testing loop.
The Report
Title: Host header injection allows password reset link poisoning, enabling token theft and account takeover
Root cause: The password reset email generation constructs the reset link's domain using the incoming request's Host header without validating it against an allowlist of legitimate application domains, allowing an attacker to cause reset emails β sent to any known victim email address, since the endpoint requires no authentication β to contain a link pointing at attacker-controlled infrastructure instead of Hazelbrook's real domain.
Reproduction: The Host-header-manipulated reset request, the resulting poisoned link observed in the email, and the full token-capture-and-replay chain demonstrating a complete account takeover β performed entirely using two self-controlled test accounts and email addresses.
Impact: Any attacker who knows a victim's email address can trigger a password reset poisoned to deliver the reset token to attacker-controlled infrastructure, achieving full account takeover if the victim clicks the resulting link, with no prior access or credential knowledge required.
Fix recommendations:
- Never construct any security-sensitive link (password reset, email verification, invite acceptance) using the request's
Hostheader; use a fixed, server-side-configured domain value instead - If multi-domain support is genuinely required, validate the
Hostheader against an explicit allowlist of legitimate application domains before using it for any purpose, rejecting the request otherwise - Bind reset tokens to the specific session or request context that generated them where feasible, adding a layer of defense beyond domain validation alone
What Happened After
Hazelbrook fixed the link generation to use a fixed, server-configured domain within two days β a fast turnaround given how directly the fix mapped to the root cause. The report closed at $9,600, rated high severity.
Why Host Header Trust Issues Are Worth Checking on Every Target
Any feature generating a link, especially one carrying a sensitive, one-time-use token, deserves a direct test of whether its domain comes from trusted server-side configuration or from a client-supplied header β the test costs one modified request, and the severity when it succeeds is almost always high, since it converts a header nobody thinks to scrutinize into a direct path toward account takeover.