Apr 1, 2026·7 min read·4 visits
A critical flaw in Payload CMS (< 3.79.1) permits unauthenticated attackers to achieve account takeover through Host header injection in password reset emails and database query partial-match misconfigurations.
Payload CMS prior to version 3.79.1 contains a critical vulnerability in its password recovery endpoints. This flaw allows an unauthenticated attacker to manipulate password reset links via Host header injection and exploit partial token matches in database adapters, leading to unauthorized account takeover.
Payload CMS is an open-source headless content management system built on Node.js. Versions prior to 3.79.1 exhibit a critical vulnerability within the password recovery workflow. The flaw specifically resides in the /api/{collection-slug}/forgot-password endpoint, which is exposed to unauthenticated network requests by design to facilitate account recovery.
The vulnerability is primarily classified under CWE-640 (Weak Password Recovery Mechanism for Forgotten Password). The application fails to strictly validate request metadata, specifically the HTTP Host header, before utilizing it to construct absolute URLs for password reset emails. This oversight allows external actors to control the destination domain of generated recovery links.
Furthermore, the vulnerability is compounded by logic flaws within the database adapters for MongoDB and Drizzle. Query operators designed for fields with the hasMany property improperly handle partial matching, allowing token search entropy reduction. This combination of input validation failures and query logic flaws facilitates unauthenticated account takeover.
The primary root cause is the reliance on user-controlled input for security-sensitive URL generation. When a user requests a password reset, the application requires an absolute URL to embed in the outbound recovery email. The vulnerable implementation derives the scheme and domain from the HTTP Host header or a user-supplied url parameter instead of relying strictly on a statically defined configuration value.
A secondary root cause exists in the query construction logic within the database adapters. The contains operator, when applied to fields possessing the hasMany attribute, utilizes partial matching mechanisms such as SQL LIKE %value% or regular expressions. This behavior becomes a critical security boundary failure when applied to high-entropy authentication tokens.
An attacker queries the database using a single character for the contains operation during a token lookup. The database engine returns the first record where the token contains that specific character. This drastically reduces the search space for the token from a secure 64-character string down to a highly probable single-character match, effectively bypassing the cryptographic token validation logic.
A tertiary issue involves polymorphic join scoping within the Drizzle SQL adapter. The adapter applies LIMIT and OFFSET clauses globally across the entire result set rather than scoping them per parent record. An attacker floods the system with reset requests, exhausting the global limit and causing legitimate user token queries to return empty result sets.
In the vulnerable implementation of the URL generation logic, the framework extracts the host directly from the incoming request headers. This design pattern violates the principle of strictly trusting server-side configuration for critical application paths. The framework effectively trusts the client-provided environment variables to build the base structure of the reset link.
The patched version enforces the usage of a strictly defined serverURL variable within the Payload configuration. All auth-related email link constructions now reference this trusted configuration property, completely ignoring the client-provided Host header or custom query parameters. This architectural change severs the attacker's ability to inject arbitrary domains.
Regarding the query operator flaw, commit fba24380578f513209a5f4811e2836a2317c33d2 addresses the partial matching mechanism. The fix modifies the contains logic applied to hasMany fields. The updated code enforces strict equality checks for sensitive identifiers rather than falling back to LIKE or Regex partial matches, preventing the entropy reduction attack.
Commit fe36dded4bcf1c54289c6687bb2308d02fbeba99 corrects the polymorphic join limits. The query builder accurately isolates LIMIT constraints to individual parent records during nested relationship resolution. This ensures that an arbitrary number of unrelated records cannot exhaust the limit boundary for legitimate users.
Exploiting the Host header injection requires the attacker to send a single, unauthenticated POST request to the forgot-password endpoint. The attacker sets the Host header to an attacker-controlled server and provides the target victim's email address in the request body. The application generates a valid cryptographic reset token and embeds it into a URL pointing to the malicious host.
The system dispatches the recovery email to the victim. Because the email originates from the legitimate service provider, it easily bypasses SPF and DKIM checks and presents a high degree of apparent authenticity to the victim. When the victim clicks the link, their client initiates a request to the attacker-controlled server.
The attacker intercepts the HTTP request and extracts the reset token from the URL path or query parameters. The attacker then submits this valid token to the legitimate application's password reset endpoint, establishes a new password, and achieves complete account takeover.
Alternatively, an attacker exploits the contains query misconfiguration by crafting a custom JSON payload for the password reset endpoint. By supplying {"where": {"resetPasswordToken": {"contains": "a"}}}, the attacker forces the database to evaluate a partial match. The system authenticates the request against the first matching user record, granting unauthorized access without requiring victim interaction.
The successful exploitation of this vulnerability results in unauthenticated, remote account takeover. An attacker compromises any user account, including administrative accounts, provided they know the target user's email address. This leads to a complete breach of confidentiality and integrity for the affected accounts and the underlying CMS platform.
With administrative access, the attacker manipulates content, alters system configurations, exfiltrates sensitive database records, and potentially achieves remote code execution depending on the specific Payload CMS configuration and active plugins. The impact scales directly with the privileges of the compromised account.
The vulnerability carries a CVSS v3.1 base score of 9.1 (Critical). The exploit requires no specialized network positioning, demands no prior authentication, and involves minimal attack complexity. While the Host header injection vector requires user interaction (the victim clicking the link), the query operator bypass functions entirely autonomously.
Furthermore, the polymorphic join limit exhaustion introduces a localized Denial of Service (DoS) condition. An attacker iteratively submits reset requests, polluting the database and preventing legitimate users from completing their password recovery workflows. This disrupts the availability of the authentication service.
The primary and most effective remediation is immediately upgrading the Payload CMS installation to version 3.79.1. This release patches all identified attack vectors, including the Host header injection, the query operator partial matching, and the polymorphic join limit scoping. Administrators must update both the payload and @payloadcms/graphql packages in their dependency trees.
Administrators must explicitly configure the serverURL property within the main Payload configuration object. This dictates the absolute origin used for all system-generated URLs, ensuring the framework strictly utilizes a known environment variable rather than evaluating client-provided HTTP headers during email link construction.
> [!NOTE]
> Relying on default configurations in earlier versions exposes the application. Verifying serverURL is correctly populated is a mandatory post-patching step.
As a defense-in-depth measure, administrators should populate the trustedOrigins configuration array. This restricts the domains that the application will interact with during cross-origin scenarios and redirect sequences. If immediate patching is not technically feasible, security teams should implement WAF rules to inspect and validate the Host header on inbound requests to the /api/*/forgot-password endpoints.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
payload Payload CMS | < 3.79.1 | 3.79.1 |
@payloadcms/graphql Payload CMS | < 3.79.1 | 3.79.1 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-640 |
| Attack Vector | Network |
| CVSS Score | 9.1 (Critical) |
| Exploit Status | Proof of Concept (PoC) |
| CISA KEV | Not Listed |
| Affected Component | Password Recovery Endpoints |
The application contains vulnerabilities in the password recovery process allowing the manipulation of reset links and query logic bypasses.