CVE-2026-39923
Executive Summary
CVE-2026-39923 is a high-severity password reset token expiry bypass vulnerability in Flarum, an open-source PHP forum platform. Affecting versions prior to 1.8.16, this flaw allows an unauthenticated attacker to reuse expired password reset tokens by submitting them directly to the password reset processing endpoint, bypassing the 24-hour token lifetime validation that is enforced only during form rendering. Successful exploitation enables an attacker to reset the password of any account — including administrator accounts — and obtain a fully authenticated session, leading to complete account takeover and potential site compromise. Given the CVSS score of 8.1 and the authentication-bypass nature of the flaw, this vulnerability represents a critical risk for all Flarum deployments not running the patched version.
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
Technical Analysis
The vulnerability resides in Flarum's password reset workflow, specifically in the SavePasswordController::handle() method. The password reset mechanism is implemented as a two-phase process:
Request Phase: A user requests a password reset for an account. The application generates a unique, cryptographically random token and stores it in the
password_tokensdatabase table, along with the associated user ID and a creation timestamp (created_at). An email is then sent to the account owner containing a link to the reset form, which includes the token as a query parameter.Reset Phase: The user clicks the email link, which renders a form via the
PasswordResetController(or similar form-rendering controller). The form includes the token (typically as a hidden field). Upon submission, theSavePasswordControllerreceives the token and password, validates the token, and updates the account password.
The critical flaw is an asymmetric validation of token expiry between the two phases. During form rendering, the application correctly validates the token's age by checking the created_at timestamp against the 24-hour expiry window. If the token exceeds 24 hours, the form render returns an error. However, in SavePasswordController::handle(), the only token validation performed is via the PasswordToken::findOrFail() call. This method retrieves the token record from the database based on the token's hash but performs no expiration timestamp validation whatsoever.
An attacker who obtains a valid password reset token (for example, by triggering a password reset for a target account and intercepting the email, or through other means such as a mailbox compromise) can therefore present that token to the reset processing endpoint at any point in the future — days, weeks, or months later — and successfully change the target account's password. The 24-hour expiry check is effectively cosmetic; it only protects the form-rendering path but not the actual password update operation.
The vulnerable code path can be summarized as:
// Vulnerable — no expiry check
class SavePasswordController {
public function handle() {
$token = PasswordToken::findOrFail($this->request->get('token'));
// Token found — but expiry NEVER checked
$user = $token->user;
$user->changePassword($this->request->get('password'));
// Password changed successfully
}
}
Attack surface: The vulnerability is exploitable by any unauthenticated network attacker who possesses a valid password reset token for a target account. The token's lifetime is not a mitigating factor. The attack requires only an HTTP POST request to the password reset endpoint (/reset/confirm or similar) with token and password parameters. The high Attack Complexity (AC:H) in the CVSS vector reflects the requirement to obtain a valid token, but once obtained, exploitation is trivial.
Exploitation Scenarios
Attackers can chain this vulnerability into a complete account takeover attack chain. The following is a realistic exploitation scenario:
Step 1 — Reconnaissance and Target Identification
The attacker identifies a Flarum forum (version ≤ 1.8.15) as a target. Fingerprinting can be performed by examining response headers (X-Powered-By, cookies such as flarum_session), or by fetching nflarum.github.io/flarum-framework and looking for version disclosure in the meta[name="generator"] tag. The attacker then selects a high-value target account such as an administrator with elevated privileges.
Step 2 — Token Acquisition
The attacker triggers a password reset for the target account by submitting a POST request to the password reset initiation endpoint (e.g., POST /reset) with the target's email address. This action is unauthenticated and rate-limit is typically not enforced on this endpoint. The reset email is delivered to the target's mailbox. The attacker must then obtain that email. Common acquisition vectors include:
- Mailbox compromise: The target uses a weak or reused password themselves.
- Mail relay misconfiguration: The forum's mail settings misdirect password reset emails to an attacker-controlled address (e.g., if the target's email is overridden in the database).
- Insider threat / shared mailbox: In smaller organizations, the forum admin's mailbox may be accessible to a disgruntled employee.
- Token leakage: The attacker may already possess a token from a prior reset request (this is the core of the 24-hour bypass).
Step 3 — Weaponization of the Expired Token
Once the attacker has the token (either directly from the email link or extracted from a prior interception), they wait beyond the 24-hour expiry window. Any period exceeding 24 hours works — there is no upper bound.
Step 4 — Password Reset Execution
After the expiry period, the attacker crafts a direct HTTP request to the reset processing endpoint:
POST /reset/confirm HTTP/1.1
Host: forum.example.com
Content-Type: application/x-www-form-urlencoded
token=[EXPIRED_TOKEN]&password=AttackerP@ssw0rd!2026&password_confirmation=AttackerP@ssw0rd!2026
Because SavePasswordController::handle() calls PasswordToken::findOrFail() without expiry validation, the token is accepted. Alternatively, the attacker can submit the token to the form-rendering URL first to obtain a valid session CSRF token, then submit the form to the processing endpoint.
Step 5 — Post-Exploitation Impact
- The attacker is now authenticated as the target user. If the target is an administrator, the attacker gains full control of the forum.
- The attacker can now upload malicious extensions (PHP files) to achieve remote code execution on the underlying server, modify all posts, exfiltrate user PII (emails, IP addresses, private messages), and pivot to the hosting infrastructure.
- The attacker can also change the forum's mail settings to intercept future password reset requests for other users, enabling a "password reset worm" that cascades across all accounts, including other administrators.
Impact Assessment
Confidentiality (High): Successful exploitation grants an authenticated session. For administrator accounts, an attacker gains complete access to all private user data, private discussions, IP addresses, email addresses, and moderation logs. The attacker can read any private data visible within the forum.
Integrity (High): The attacker can modify forum content — editing, deleting, or injecting posts. As an administrator, they can alter user profiles, change forum configuration, install malicious extensions, and modify the underlying server files if PHP upload is enabled.
Availability (High): An attacker with administrator access can deface the forum, lock out legitimate users, disable the site, or destroy the database.
Attack Complexity (High): The attacker must obtain a valid password reset token, which requires either mailbox access or interception of a reset email. This elevates the complexity but does not prevent exploitation — once a token is obtained, the 24-hour window poses no barrier.
Scope / Blast Radius: All Flarum deployments running versions prior to 1.8.16 with password reset functionality enabled (default configuration). The vulnerability is classifiable under CWE-324 (Use of a Cryptographic Primitive with a Risky Implementation) — the token is cryptographically sound but the validation logic around its expiry is flawed.
Exploitability in the Wild: No public PoC or active exploitation has been reported as of this writing. The EPSS score has not yet been assigned by FIRST.org. The vulnerability is not listed on CISA's Known Exploited Vulnerabilities (KEV) catalog. However, the flaw is trivial to exploit once a token is obtained, and it is reasonable to expect weaponization shortly after public disclosure.
Detection Guidance
Sigma Rule
title: Flarum Password Reset Token Expiry Bypass Exploitation
id: c8f3a2d1-7e5b-4a1c-9f2d-3b6e8a0c4f1e
status: experimental
description: Detects attempts to submit expired password reset tokens to the Flarum reset processing endpoint
author: Valters IT Security Team
date: 2026-02-10
logsource:
category: web
product: nginx
service: http
detection:
selection_uri:
cs-method: POST
cs-uri-extension: contains: reset
selection_response:
http.response.status_code: 200
selection_body:
http.request.body: contains: password
condition: selection_uri and selection_response and selection_body
falsepositives:
- Legitimate password reset submissions within the valid 24-hour window
level: high
tags:
- attack.reconnaissance
- attack.credential-access
- cve.2026.39923
YARA Rule
rule Flarum_Password_Token_Expiry_Bypass_CVE_2026_39923
{
meta:
author = "Valters IT Security"
date = "2026-02-10"
description = "Detects Flarum source code lacking expiry validation in SavePasswordController"
cve = "CVE-2026-39923"
severity = "high"
strings:
$a = "class SavePasswordController" ascii nocase
$b = "PasswordToken::findOrFail" ascii nocase
$c = "created_at" ascii nocase
$d = "changePassword" ascii nocase
$missing_expiry = "expires_at" ascii nocase
condition:
all of them and not $missing_expiry
}
Suricata IDS Rule
alert http any any -> any any (msg:"ET EXPLOIT Flarum Password Reset Token Expiry Bypass Attempt (CVE-2026-39923)"; flow:established,to_server; http.method; content:"POST"; http.uri; content:"reset"; http.request_body; content:"token="; http.request_body; content:"password="; http.request_body; pcre:"/password_confirmation=/i"; http.host; content:"!"; metadata: former_category EXPLOIT; classtype: attempted-admin; sid: 202639923001; rev: 1; reference: cve, CVE-2026-39923;)
Elastic Detection
sequence by source.ip
with maxspan=10m
[ http where event.category == "web" and http.request.method == "POST"
and url.path contains "reset" and request.body contains "token="
and request.body contains "password=" ]
Additional rule for the server-side anomaly:
process where event.action == "executed" and
process.parent.name == "php-fpm" and
process.args : "*SavePasswordController*"
Splunk SPL Query
index=web sourcetype=access_combined
| where method="POST" AND uri_path LIKE "%reset%"
| search body="*token=*" body="*password=*"
| rex field=uri_path "(?<endpoint>/[^?]+)"
| eval suspicious = if(endpoint=="reset/confirm" OR endpoint=="password/reset", 1, 0)
| where suspicious=1
| stats count as attempts by src_ip, uri_path, _time
| where attempts > 1
| sort -_time
Wazuh OSSEC Rule
<group name="flarum,web_attacks,">
<rule id="100230" level="10">
<if_sid>30008</if_sid>
<match>POST</match>
<match>reset</match>
<match>token=</match>
<match>password=</match>
<description>Flarum password reset token expiry bypass attempt (CVE-2026-39923)</description>
<group>cve-2026-39923,</group>
<mitre>
<id>T1110</id>
</mitre>
</rule>
</group>
Mitigation & Patch Guidance
Patch Confidence: 2/4 (CLAIMED) — A single source (NVD) claims a patched version exists (1.8.16), but this has not been independently confirmed by the Flarum project team on their official advisory channels at the time of writing.
1. Immediate Mitigations (if upgrade is not immediately possible):
- Disable automatic password reset functionality: Edit the Flarum configuration to redirect the
/resetroute to a 404 page, or use a Web Application Firewall (WAF) rule to block POST requests to/reset/confirmand/resetendpoints. This eliminates the attack vector entirely. - Rate-limit the password reset endpoint: Configure nginx or the WAF to rate-limit POST requests to
/reset*(e.g., max 5 requests per IP per hour). This limits an attacker's ability to brute-force or abuse the endpoint. - Restrict access to the admin control panel: Limit administrative login to trusted IP ranges or enforce MFA on all administrator accounts. While this doesn't fix the vulnerability, it reduces the blast radius if an admin account is compromised.
- Monitor password reset emails: Review the forum's mail logs for any password reset requests sent to unknown or external domains. If a reset email goes to an unexpected recipient, investigate immediately.
2. Patch / Upgrade Steps:
- Upgrade Flarum to version 1.8.16 or later. This is the version in which the fix is claimed to be included.
- Upgrade command (PHP/composer):
composer update flarum/core --with-all-dependencies php flarum migrate php flarum cache:clear - Verify the patch: After the upgrade, check the
SavePasswordControllersource code to confirm that expiry validation is now present, e.g., a check such asif ($token->created_at->diffInHours(now()) > 24) { throw new ...; }. If the check is missing, the patch is incomplete. - Test the patch: Trigger a password reset, wait 25 hours, then attempt to use the token. The request should fail with a token-expired error.
3. Long-Term Hardening:
- Rotate all administrator passwords after applying the fix, in case any previously intercepted reset tokens exist.
- Enable MFA for all forum accounts, particularly moderators and administrators, to reduce the impact of any future credential theft.
- Regularly audit the
password_tokenstable for tokens older than 24 hours that remain active — a sign of an attempt to use expired tokens. - Subscribe to Flarum's security advisories and monitor the project's GitHub release notes for any additional vulnerability patches.
- Ensure the forum's mail server configuration uses TLS and that password reset emails are sent only to verified, non-disposable email domains.
References
NVD — CVE-2026-39923: https://nvd.nist.gov/vuln/detail/CVE-2026-39923 (Primary source; contains the vulnerability description and the claimed fixed version 1.8.16)
Flarum GitHub — Release Notes for v1.8.16: https://github.com/flarum/framework/releases/tag/v1.8.16 (Pending confirmation; check here for the official fix commit and changelog entry)
CWE-324 — Use of a Cryptographic Primitive with a Risky Implementation: https://cwe.mitre.org/data/definitions/324.html (Weakness categorization for the expired-token acceptance flaw)
FIRST.org — EPSS (Pending Score): https://www.first.org/epss/ (The EPSS score for CVE-2026-39923 has not yet been assigned; this page will provide a real-time risk metric once available)
CISA Known Exploited Vulnerabilities Catalog: https://www.cisa.gov/known-exploited-vulnerabilities-catalog (CVE-2026-39923 is NOT currently in the KEV catalog; if added, a deadline for patching will be set)
Threat Actor Campaigns: As of this report date, no threat actor campaigns specifically targeting CVE-2026-39923 have been observed in the wild. The vulnerability is not in the CISA KEV list, and no exploit code has been publicly released. However, the trivial nature of the post-token-exploitation (a single HTTP request) combined with the high impact of administrator account takeover makes this a high-priority candidate for future weaponization. Security teams should prioritize patching before such weaponization occurs.