Skip to main content
CVE Vulnerability Database
Vulnerability Database/CVE-2025-58065

CVE-2025-58065: Flask-AppBuilder Auth Bypass Vulnerability

CVE-2025-58065 is an authentication bypass flaw in Flask-AppBuilder that allows disabled users to reset passwords and create JWT tokens when using OAuth or LDAP authentication. This article covers technical details, affected versions, impact assessment, and mitigation strategies.

Published:

CVE-2025-58065 Overview

CVE-2025-58065 is an authentication weakness in Flask-AppBuilder, a Python application development framework built on Flask. Versions prior to 4.8.1 register the password reset endpoint regardless of the configured authentication backend. When administrators configure OAuth, LDAP, or another non-database authentication method, the reset routes remain accessible even though they are hidden in the UI. An enabled user can reset their local password and mint JSON Web Tokens (JWTs), retaining access after being disabled at the upstream identity provider. The flaw maps to Improper Authentication [CWE-287].

Critical Impact

Users disabled at an external identity provider can retain application access by resetting their local password and generating valid JWT tokens, breaking the integrity of centralized identity governance.

Affected Products

  • Flask-AppBuilder versions prior to 4.8.1
  • Deployments configured with AUTH_OAUTH, AUTH_LDAP, AUTH_OID, or AUTH_REMOTE_USER
  • Applications built on Flask-AppBuilder that expose JWT token issuance

Discovery Timeline

  • 2025-09-11 - CVE-2025-58065 published to the National Vulnerability Database
  • 2026-06-17 - Last updated in NVD database

Technical Details for CVE-2025-58065

Vulnerability Analysis

Flask-AppBuilder registers views during security manager initialization. Prior to version 4.8.1, ResetPasswordView and ResetMyPasswordView were registered unconditionally via add_view_no_menu, regardless of the value of AUTH_TYPE. Although the UI links to these views are only rendered when AUTH_DB is active, the underlying HTTP routes remain reachable at their default URLs.

In deployments that use external identity providers, an authenticated user can invoke the reset endpoint to change their locally stored password hash. If the account is later disabled at the identity provider, the attacker can still authenticate through any database-backed path that consults the local password, including endpoints that issue JWTs. This defeats the centralized deprovisioning workflow security teams rely on when using single sign-on.

Root Cause

The root cause is a missing conditional guard around view registration in flask_appbuilder/security/manager.py. View registration for the password reset routes did not check self.auth_type == AUTH_DB before being attached to the application, leaving the routes registered for all authentication backends.

Attack Vector

Exploitation requires an authenticated session belonging to an enabled application user. The attacker navigates directly to the hidden reset URL, submits a new password, and subsequently uses the local credential to obtain a JWT even after the identity provider has revoked their account.

python
             if self.registeruser_view:
                 self.appbuilder.add_view_no_menu(self.registeruser_view)
 
-        self.appbuilder.add_view_no_menu(self.resetpasswordview())
-        self.appbuilder.add_view_no_menu(self.resetmypasswordview())
         self.appbuilder.add_view_no_menu(self.userinfoeditview())
 
         if self.auth_type == AUTH_DB:
             self.user_view = self.userdbmodelview
             self.auth_view = self.authdbview()
+            self.appbuilder.add_view_no_menu(self.resetpasswordview())
+            self.appbuilder.add_view_no_menu(self.resetmypasswordview())
 
         elif self.auth_type == AUTH_LDAP:
             self.user_view = self.userldapmodelview
# Source: https://github.com/dpgaspar/Flask-AppBuilder/commit/a942a9cc5775752f9a02f97fd8198dd288fa93ee

The patch moves the two add_view_no_menu calls inside the AUTH_DB branch so the reset routes are only registered when database authentication is active.

Detection Methods for CVE-2025-58065

Indicators of Compromise

  • HTTP requests to /resetmypassword/form or /resetpassword/form in applications configured with non-database authentication backends
  • Successful password reset events for users who authenticate exclusively through OAuth, LDAP, or OIDC
  • JWT tokens issued to accounts that have been disabled at the upstream identity provider

Detection Strategies

  • Correlate reset endpoint access with the value of AUTH_TYPE in the Flask-AppBuilder configuration; any hits when AUTH_TYPE != AUTH_DB warrant investigation
  • Compare application login and token issuance events against identity provider deprovisioning events to identify users authenticating after account disablement
  • Alert on password change events for user records whose login_count for the database path is zero prior to the reset

Monitoring Recommendations

  • Enable verbose access logging on the reverse proxy for all URLs matching resetpassword and resetmypassword
  • Forward Flask-AppBuilder audit logs and identity provider events to a central analytics platform for cross-source correlation
  • Track version metadata for deployed Flask-AppBuilder instances to identify hosts still running versions prior to 4.8.1

How to Mitigate CVE-2025-58065

Immediate Actions Required

  • Upgrade Flask-AppBuilder to version 4.8.1 or later across all environments
  • Audit user accounts for unexpected local password changes since deployment of non-database authentication
  • Rotate or invalidate existing JWT signing keys if evidence of exploitation is found, forcing token reissuance

Patch Information

The fix is delivered in Flask-AppBuilder v4.8.1 via pull request #2384 and commit a942a9cc. Additional context is available in the GitHub Security Advisory GHSA-765j-9r45-w2q2.

Workarounds

  • Manually unregister the reset password routes in application startup code when AUTH_TYPE is not AUTH_DB
  • Block access to /resetpassword and /resetmypassword URLs at the reverse proxy, web application firewall, or ingress controller
  • Implement identity provider webhook processing that removes disabled users from the Flask-AppBuilder ab_user table
bash
# Example NGINX block for non-AUTH_DB deployments
location ~* ^/(resetpassword|resetmypassword) {
    return 403;
}

Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

Default Legacy - Prefooter | Experience the World’s Most Advanced Cybersecurity Platform

Experience the Most Advanced Cybersecurity Platform

See how the world’s most intelligent, autonomous cybersecurity platform can protect your organization today and into the future.