CVE-2025-3930
Executive Summary
CVE-2025-3930 is a critical security vulnerability in Strapi, an open-source headless CMS. The flaw resides in the improper invalidation of JSON Web Tokens (JWT) after a user logs out or their account is deactivated. An attacker who has stolen or intercepted a valid JWT can reuse it until its natural expiration (default 30 days). Furthermore, the existence of the /admin/renew-token endpoint allows an attacker to indefinitely extend the token's validity, effectively granting persistent unauthorized access to the Strapi admin panel. This vulnerability has been fixed in Strapi version 5.24.1.
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
Strapi is distributed as an npm package (@strapi/strapi). Organizations using Strapi should immediately audit their dependency tree to ensure they are not using a vulnerable version. The fix is included in version 5.24.1. The npm advisory for this CVE should be monitored for further details.
CVSS Breakdown & Threat Level
While a CVSS score is not provided, the impact is severe. The vulnerability allows for:
- Confidentiality Impact: High (attacker can access all admin data)
- Integrity Impact: High (attacker can modify content, users, and settings)
- Availability Impact: High (attacker can disrupt the CMS)
- Attack Vector: Network
- Attack Complexity: Low (requires token interception)
- Privileges Required: None (if token is stolen)
- User Interaction: None
- Scope: Unchanged
Given the ease of exploitation and the potential for persistent access, this vulnerability should be treated as Critical.
Deep Dive & Mechanics
The vulnerability is a classic case of Insufficient Session Expiration (CWE-613). Strapi uses JWTs for authentication. When a user logs in, a JWT is issued. The intended security model is that the JWT is the sole bearer credential. However, the system fails to maintain a server-side blacklist or revocation list for tokens.
- Token Issuance: Upon successful login, Strapi issues a JWT with an expiration time (default 30 days).
- Logout/Deactivation: When a user logs out or an admin deactivates an account, the server does not invalidate the issued JWT. The token remains valid until its
expclaim. - Token Renewal: The
/admin/renew-tokenendpoint is designed to allow a user to refresh their token before it expires. This endpoint accepts a valid (non-expired) JWT and returns a new JWT with a fresh expiration date. An attacker with a stolen token can call this endpoint repeatedly, effectively creating a never-ending session. - Exploitation: An attacker who has intercepted a JWT (e.g., via a man-in-the-middle attack, XSS, or log file) can use it to access the Strapi admin panel. They can then use the
/admin/renew-tokenendpoint to keep the token alive indefinitely.
Vulnerable vs. Patched Code
The vulnerable code likely resides in the authentication middleware and the logout/deactivation handlers.
Vulnerable Code (Conceptual):
// In auth middleware (vulnerable)
function authenticate(token) {
try {
const decoded = jwt.verify(token, secret);
// No check against a blacklist or session store
return decoded;
} catch (err) {
return null;
}
}
// In logout handler (vulnerable)
function logout(req, res) {
// Simply clears the cookie, but does not invalidate the token
res.clearCookie('jwt');
res.status(200).json({ message: 'Logged out' });
}
Patched Code (Conceptual):
// In auth middleware (patched)
const invalidatedTokens = new Set(); // Or a database table
function authenticate(token) {
try {
if (invalidatedTokens.has(token)) {
return null;
}
const decoded = jwt.verify(token, secret);
return decoded;
} catch (err) {
return null;
}
}
// In logout handler (patched)
function logout(req, res) {
const token = req.cookies.jwt;
if (token) {
invalidatedTokens.add(token); // Add to blacklist
}
res.clearCookie('jwt');
res.status(200).json({ message: 'Logged out' });
}
// In renew-token handler (patched)
function renewToken(req, res) {
const oldToken = req.body.token;
if (invalidatedTokens.has(oldToken)) {
return res.status(401).json({ error: 'Token invalidated' });
}
// ... proceed with renewal logic
}
Exploitation & Lab Setup
Red Team (Attacker)
Objective: Gain persistent access to the Strapi admin panel.
Prerequisites:
- A valid JWT token (stolen via XSS, MITM, or other means).
- Network access to the Strapi instance.
Steps:
- Intercept Token: Obtain a valid JWT. For example, via a compromised browser session or a network capture.
- Access Admin Panel: Use the token to make authenticated requests to the Strapi admin API.
curl -H "Authorization: Bearer <STOLEN_JWT>" https://target-strapi.com/admin/users - Renew Token: To ensure persistent access, call the
/admin/renew-tokenendpoint before the token expires.
This will return a new JWT with a fresh expiration date.curl -X POST -H "Content-Type: application/json" -d '{"token": "<STOLEN_JWT>"}' https://target-strapi.com/admin/renew-token - Maintain Access: Repeat step 3 periodically to keep the session alive indefinitely.
Blue Team (Defender)
Objective: Detect and prevent exploitation of this vulnerability.
Detection:
- Monitor for repeated calls to
/admin/renew-tokenfrom the same IP or user agent. - Alert on successful authentication events that do not have a corresponding login event (e.g., token reuse).
- Monitor for access to the admin panel from unusual IP addresses or at unusual times.
Prevention:
- Immediate: Upgrade to Strapi version 5.24.1 or later.
- Mitigation (if upgrade is not possible):
- Implement a Web Application Firewall (WAF) rule to block requests to
/admin/renew-tokenfrom non-authenticated sessions. - Reduce the JWT expiration time to a very short duration (e.g., 15 minutes) to limit the window of opportunity.
- Implement a server-side session store and invalidate tokens upon logout.
- Implement a Web Application Firewall (WAF) rule to block requests to
Indicators of Compromise (IOCs)
- Network:
- Repeated POST requests to
/admin/renew-tokenwith a valid JWT. - Successful API calls to
/admin/*endpoints without a preceding login event.
- Repeated POST requests to
- Logs:
- Strapi access logs showing multiple token renewals for the same user.
- Authentication logs showing successful logins from unusual IP addresses.
- File System:
- No specific file-based IOCs, as this is a logic flaw.
Detection Engineering
YARA Rule
rule Detect_Strapi_JWT_Reuse {
meta:
description = "Detects potential exploitation of CVE-2025-3930 by looking for repeated token renewal requests"
author = "Valters IT"
date = "2025-04-10"
hash = "N/A"
strings:
$renew_endpoint = "/admin/renew-token"
$jwt_pattern = /eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+/
condition:
#renew_endpoint > 5 and #jwt_pattern > 0
}
Sigma Rule
title: Strapi JWT Token Reuse Detection
id: 12345678-1234-1234-1234-123456789012
status: experimental
description: Detects multiple requests to the Strapi /admin/renew-token endpoint from the same source IP, indicating potential JWT reuse.
author: Valters IT
date: 2025/04/10
logsource:
category: webserver
product: nginx
detection:
selection:
cs-method: 'POST'
cs-uri-stem: '/admin/renew-token'
condition: selection | count() by src_ip > 5
falsepositives:
- Legitimate users with multiple browser tabs
level: high
Suricata Rule
alert http $HOME_NET any -> $EXTERNAL_NET any (
msg:"Strapi JWT Token Reuse Attempt";
flow:to_server;
content:"POST";
http_method;
content:"/admin/renew-token";
http_uri;
content:"token";
http_client_body;
threshold: type both, track by_src, count 5, seconds 60;
sid:1000001;
rev:1;
)
Remediation & Mitigation
- Immediate Action: Upgrade Strapi to version 5.24.1 or later. This is the only complete fix.
- Short-Term Mitigation (if upgrade is delayed):
- Reduce JWT Expiration: Set the JWT expiration time to a very short duration (e.g., 15 minutes) in the Strapi configuration.
- Disable Token Renewal: If possible, disable the
/admin/renew-tokenendpoint via a reverse proxy or WAF rule. - Implement Session Blacklist: If you have custom middleware, implement a server-side blacklist for invalidated tokens.
- Long-Term Security:
- Implement a robust session management system that uses server-side sessions instead of relying solely on JWTs.
- Regularly audit and update all dependencies.
- Monitor for any unusual activity in the Strapi admin panel.
Valters IT
Securing Your Digital Future