Oct 6, 2026·6 min read·3 visits
Payload CMS utilized a PBKDF2 configuration that requested a 512-byte output length. Because SHA-256 produces 32-byte blocks, the server performed 16x more work than necessary, whereas offline attackers could skip 15 of those blocks to verify guesses, granting them a 16x speedup advantage.
Payload CMS was discovered to use an insecure default configuration for its password-hashing mechanism. The system requested a 512-byte key from PBKDF2-HMAC-SHA256 with 25,000 iterations, creating a severe cryptographic asymmetry. While the defending server sequentially computed 16 blocks of key material (equivalent to 400,000 internal iterations), an offline attacker only needed to compute the first 32-byte block to verify password guesses. This allowed offline attackers to crack stolen database hashes 16 times faster than intended by the security design.
Payload CMS is a headless content management system built with Node.js and TypeScript. In affected versions starting from 3.0.0 up to 3.90.0, the core authentication handler utilized an insecurely configured key derivation function. Specifically, the implementation relied on PBKDF2-HMAC-SHA256 to hash and store user passwords securely.
To increase defensive resource requirements, the engine was configured with 25,000 iterations. However, it also requested a derived key size of 512 bytes. Because the underlying SHA-256 hash function natively produces digests of only 32 bytes, requesting a 512-byte key size introduced a fundamental performance flaw into the verification pipeline.
The resulting system suffered from computational asymmetry, where the legitimate defender was significantly penalized in performance, while offline attackers holding a copy of the database hashes could bypass the penalty entirely. This design flaw significantly lowered the bar for successful credential recovery via brute-force or dictionary attacks against compromised database dumps.
The root cause of this vulnerability lies in a mismatch between the requested output key size of the PBKDF2 function and the native block digest size of the underlying hash function (SHA-256). PBKDF2 (Password-Based Key Derivation Function 2) derives keys by executing an internal pseudorandom function (PRF) over multiple iterations.
When the requested key length ($dkLen$) is greater than the native digest size of the PRF ($hLen$), PBKDF2 must run its iteration loop multiple times to generate independent blocks of key material. The total number of blocks ($l$) is calculated as $l = \lceil dkLen \div hLen \rceil$. For a requested key size of 512 bytes using SHA-256 ($hLen = 32$):
$$l = \lceil 512 \div 32 \rceil = 16 \text{ blocks}$$
These 16 blocks are generated sequentially, meaning the defender must compute $16 \times 25,000 = 400,000$ iterations of HMAC-SHA256 to verify a user's password. Crucially, each block is derived independently. An offline attacker attempting to crack a stolen hash does not need to verify the entire 512 bytes. Because the first 32-byte block contains sufficient entropy to confirm a password match, the attacker only computes the first block, requiring only 25,000 iterations. This creates a 16x speedup advantage for the attacker over the defender.
In vulnerable versions of the application, the generatePasswordSaltHash.ts file configured crypto.pbkdf2 using hardcoded parameters that requested the 512-byte key length:
// Vulnerable Code Path
function pbkdf2Promisified(password: string, salt: string): Promise<Buffer> {
return new Promise((resolve, reject) =>
crypto.pbkdf2(password, salt, 25000, 512, 'sha256', (err, hashRaw) =>
err ? reject(err) : resolve(hashRaw), // Requests 512 bytes (underlying SHA-256 is 32 bytes)
),
)
}The patch applied in commit 1bc76e540a10f3ec88340f40591a57dae7456d01 resolved this design flaw by increasing the iteration count to modern OWASP recommendations (600,000) and reducing the requested key length to 32 bytes. This ensures that only one block of key material is generated, eliminating the sequential generation overhead.
// Patched Configuration
const currentPasswordHashPrefix = 'pbkdf2-sha256-v1:'
const currentPasswordHashIterations = 600000
const currentPasswordHashKeyLength = 32
const legacyPasswordHashIterations = 25000
const legacyPasswordHashKeyLength = 512Additionally, to handle legacy hashes seamlessly without forcing a global password reset, an opportunistic upgrade mechanism was introduced inside the authentication routines (login.ts and authenticate.ts). When a user logs in successfully using a legacy hash, the system verifies the password using the legacy parameters, generates a new hash using the upgraded parameters, and updates the database atomically. To prevent concurrency issues or race conditions during this update, the Drizzle database adapter was patched (updateOne.ts) to enforce conditional update constraints.
Exploitation of this vulnerability occurs offline and does not require direct interaction with the running Payload CMS server. The attack flow begins when an attacker obtains an unauthorized copy of the application database (e.g., via SQL injection, directory traversal, or exposed backup files).
Upon parsing the database, the attacker identifies stored password hashes lacking the modern pbkdf2-sha256-v1: prefix, indicating they were generated under the legacy configuration. Instead of running a standard hashing derivation utility that calculates all 512 bytes, the attacker configures a high-performance cracking platform (such as Hashcat or John the Ripper) to run a custom kernel.
This optimized kernel is configured to truncate the target hashes and only verify candidate passwords against the first 32 bytes of the stored hash. Because only one block of PBKDF2 is calculated, the hardware cracks passwords at a rate 16 times higher than the target system's performance would suggest, vastly reducing the time required to recover plaintext credentials.
The CVSS v4 score is evaluated at 5.7 (Medium). The attack vector is classified as Local (AV:L) because obtaining the raw password hashes generally requires local or pre-existing high-privilege access to the target system's database or filesystems.
The real-world security impact is highly significant for environments that rely on complex user management and permission structures. Successful cracking of database hashes allows attackers to recover administrative credentials. Once administrative access is achieved on a headless CMS like Payload, attackers can manipulate content, execute unauthorized API queries, or leverage server-side privileges to move laterally within the hosting environment.
The primary remediation strategy is upgrading all Payload CMS dependencies to versions >= 3.90.0 or >= 4.0.0-canary.34. These releases implement the modern hashing standard and enforce secure PBKDF2 parameters.
Upgrades are backwards-compatible and handle existing hashes transparently. When users log in, the application will automatically migrate their password hashes to the secure standard. For administrators who cannot upgrade immediately, the following defensive practices are strongly recommended:
CVSS:4.0/AV:L/AC:H/AT:P/PR:H/UI:N/VC:H/VI:L/VA:N/SC:N/SI:N/SA:N| Product | Affected Versions | Fixed Version |
|---|---|---|
payload payloadcms | >= 3.0.0, < 3.90.0 | 3.90.0 |
payload payloadcms | >= 4.0.0-canary.0, < 4.0.0-canary.34 | 4.0.0-canary.34 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-916 |
| Attack Vector | Local |
| CVSS v4 Score | 5.7 (Medium) |
| EPSS Score | Not listed |
| Exploit Status | none |
| CISA KEV Status | Not Listed |
The application generates hashes for passwords using parameters that make the hashing process computationally inexpensive, facilitating offline dictionary or brute-force attacks.
An Improper Access Control vulnerability (CWE-284) in Payload CMS prior to version 3.90.0 and 4.0.0-canary.34 allows authenticated, low-privileged users to bypass field-level access control restrictions and overwrite the password of other accounts, leading to complete account takeover and privilege escalation.
VectorFreed identifies a critical Use-After-Free (UAF) memory corruption vulnerability in librsvg (CVE-2026-96889), which manifests when parsing structured SVG documents containing nested XML inclusions (XIncludes) and duplicate entity declarations. The flaw results from an entity ownership conflict where librsvg prematurely deallocates an xmlEntity structure still actively referenced by the underlying libxml2 parser context. When transitively compiled into downstream applications such as the high-performance sharp image processing library, this vulnerability facilitates denial of service and unauthenticated remote code execution on the host operating system.
CVE-2026-102275 (GHSA-x33g-cr3x-6449) is a public/private key identity confusion vulnerability in PyJWT versions 2.1.0 through 2.14.0. When importing Octet Key Pair (OKP) JSON Web Keys (JWKs) representing Ed25519 or Ed448 curves, PyJWT fails to verify that the public parameter 'x' matches the private parameter 'd'. An attacker can construct a hybrid JWK combining a victim's public key with the attacker's private key. In protocols like DPoP that bind sessions via public key thumbprints, this allows the attacker to authenticate as the victim while signing proofs with their own private key, fully bypassing sender-constrained security guarantees.
An authentication bypass and privilege escalation vulnerability exists in Filament (filamentphp/filament) due to missing password verification during multi-factor authentication (MFA) setup and management. An attacker with access to an active session can modify or disable MFA, leading to account hijacking.
A critical Denial of Service (DoS) vulnerability exists in @socket.io/cluster-engine before version 0.1.1. Unauthenticated remote attackers can crash the server process by supplying inherited prototype property names as session identifiers.
A vulnerability in the Client-Side Field-Level Encryption (CSFLE) component of the MongoDB Python Driver (PyMongo) allows an attacker with database write access to trigger local Unix domain socket connections. By manipulating the Key Management Service (KMS) endpoint configuration inside the key vault collection to end with a '.sock' extension, an attacker forces the application to perform a Server-Side Request Forgery (SSRF) against internal Unix domain sockets.