Skip to content
winfunc
Disclosure record · Mattermost

Oversized password login DoS in legacy password comparison (CVE-2026-24458)

Login password comparison paths accepted attacker-supplied passwords without enforcing the maximum password length first

MattermostCVE-2026-24458DisclosedSource record

CVSS 3.1 base score

High
7.5/ 10

Vector: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H

Record

Project
Mattermost
Severity
High7.5
CVE
CVE-2026-24458
Disclosed
Trace
4 steps

Summary

Source

login

server/channels/api4/user.go:2060

Sink

BCrypt.CompareHashAndPassword

server/channels/app/password/hashers/bcrypt.go:75

Mattermost validates password length when passwords are created or changed, but affected login comparison paths did not consistently apply the same maximum-length guard before invoking password verification. In modern 11.x branches, App.checkUserPassword parses the stored hash and dispatches to the matching hasher. PBKDF2 already enforced PasswordMaxLengthBytes, but the legacy bcrypt hasher's CompareHashAndPassword did not check input length before calling bcrypt.CompareHashAndPassword. In the 10.11 line, the older users.ComparePassword helper similarly called bcrypt.CompareHashAndPassword before checking model.PasswordMaximumLength.

The fix adds explicit maximum-length checks in the comparison path before expensive password verification is attempted: PR #35092 / commit 7201f42d955f1bc44719b862132546626b60a180 for the 11.x legacy hasher path, with backports #35117, #35126, and #35127; and PR #35062 / commit 0655a633546037107a1e0f3243c542fbaea0901f for the 10.11 comparison helper.

Severity

Attack vectorAV
Network
Attack complexityAC
Low
Privileges requiredPR
None
User interactionUI
None
ScopeS
Unchanged
ConfidentialityC
None
IntegrityI
None
AvailabilityA
High

Metric values as published in the disclosure vector. Meters show how far each value raises exposure.

Source-to-sink trace

  1. Source · attacker-controlledserver/channels/api4/user.go:2060

    login

  2. Step 01server/channels/api4/user.go:2060-2096

    The unauthenticated login endpoint parses the attacker-supplied JSON body, including password, and passes it into authentication.

    go
    props := model.MapFromJSON(r.Body)
    loginId := props["login_id"]
    password := props["password"]
    mfaToken := props["token"]
    
    user, err = c.App.AuthenticateUserForLogin(c.AppContext, id, loginId, password, mfaToken, "", ldapOnly)
    
  3. Step 02server/channels/app/authentication.go:65-78

    checkUserPassword obtains the hasher from the stored hash and compares the attacker-supplied plaintext password with it.

    go
    hasher, phc, err := hashers.GetHasherFromPHCString(user.Password)
    if err != nil {
        return model.NewAppError("checkUserPassword", "api.user.check_user_password.invalid_hash.app_error", nil, "user_id="+user.Id, http.StatusUnauthorized)
    }
    
    err = hasher.CompareHashAndPassword(phc, password)
    
  4. Step 03server/channels/app/password/hashers/bcrypt.go:75-85

    The 11.x fix adds a maximum-length check to the legacy bcrypt comparison wrapper before calling bcrypt.CompareHashAndPassword.

    go
    func (b BCrypt) CompareHashAndPassword(hash phcparser.PHC, password string) error {
        if len(password) > PasswordMaxLengthBytes {
            return ErrPasswordTooLong
        }
    
        err := bcrypt.CompareHashAndPassword([]byte(hash.Hash), []byte(password))
        if errors.Is(err, bcrypt.ErrMismatchedHashAndPassword) {
            return ErrMismatchedHashAndPassword
        }
    
        return err
    }
    
  5. Step 04server/channels/app/users/password.go:23-31 (release-10.11 fix)

    The 10.11 fix adds the same fail-fast length check to the older ComparePassword helper before the bcrypt comparison call.

    go
    func ComparePassword(hash string, password string) error {
        if password == "" || hash == "" {
            return errors.New("empty password or hash")
        }
    
        if len(password) > model.PasswordMaximumLength {
            return NewErrInvalidPassword("model.user.is_valid.pwd_max_length.app_error")
        }
    
        return bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
    }
    
  6. Sinkserver/channels/app/password/hashers/bcrypt.go:75

    BCrypt.CompareHashAndPassword

Impact

Reported impact

Oversized login attempts can impose disproportionate work on the server's authentication path, resulting in denial of service for legitimate login attempts. The fix makes oversized values fail closed before hash comparison.

Attack surface

The login API (POST /api/v4/users/login) for accounts whose stored hashes use the affected comparison path. The 11.x fix is specifically the legacy bcrypt hasher path; the 10.11 fix is the older bcrypt comparison helper.

Preconditions

Network access to the login endpoint. The attacker can submit oversized password values for an existing login ID. No valid password is required for the resource-exhaustion attempt.

Attack path

  1. 1

    Prepare a login request with a very large password string.

  2. 2

    Submit repeated requests to /api/v4/users/login for an existing account that reaches an affected comparison path.

  3. 3

    The vulnerable code enters password verification before rejecting password length.

  4. 4

    Parallel requests consume CPU and memory in authentication workers, degrading login availability.

Proof of concept

Reproduction5 stages
  1. 01

    Environment setup

    Use a vulnerable release before the relevant fix for the release line: before #35092 for 11.x legacy bcrypt accounts or before #35062 for 10.11.

  2. 02

    Target configuration

    Have a reachable Mattermost login endpoint and a known existing login ID. For 11.x, the clearest affected path is an account with a legacy bcrypt-formatted stored password.

  3. 03

    Exploit delivery

    Send repeated POST /api/v4/users/login requests with a multi-megabyte password value and the target login_id.

  4. 04

    Expected response

    Vulnerable builds spend expensive verification work on the oversized password before returning an authentication error. Fixed builds return through the password-length error path before invoking the hasher comparison.

  5. 05

    Outcome

    The maximum password length is enforced consistently during comparison, not only during password creation or update.

Remediation

Guidance

Apply password length validation before any password verification primitive, including legacy-hash comparison paths and release-line helper functions. Keep the limit enforcement close to the comparison function so every caller gets the fail-fast behavior.

Before and after−8+12
func (b BCrypt) CompareHashAndPassword(hash phcparser.PHC, password string) error {    err := bcrypt.CompareHashAndPassword([]byte(hash.Hash), []byte(password))    if errors.Is(err, bcrypt.ErrMismatchedHashAndPassword) {        return ErrMismatchedHashAndPassword    }     return err}func (b BCrypt) CompareHashAndPassword(hash phcparser.PHC, password string) error {    if len(password) > PasswordMaxLengthBytes {        return ErrPasswordTooLong    }     err := bcrypt.CompareHashAndPassword([]byte(hash.Hash), []byte(password))    if errors.Is(err, bcrypt.ErrMismatchedHashAndPassword) {        return ErrMismatchedHashAndPassword    }     return err}
View original source record

Check the upstream record for the project's current remediation status.

Your codebase

Investigate the paths that matter in your codebase.

Winfunc can trace relevant code paths, preserve supporting evidence, and prepare remediation suggestions for engineering review within an agreed scope.