VYPR
Low severityNVD Advisory· Published Jun 12, 2025· Updated Jun 12, 2025

vantage6 lacks brute-force protection on change password functionality

CVE-2025-43863

Description

vantage6 is an open source framework built to enable, manage and deploy privacy enhancing technologies like Federated Learning and Multi-Party Computation. If attacker gets access to an authenticated session, they can try to brute-force the user password by using the change password functionality: they can call that route infinitely which will return the message that password is wrong until it is correct. This vulnerability is fixed in 4.11.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
vantage6PyPI
< 4.11.04.11.0

Affected products

1

Patches

1
e0f1841b310f

Merge commit from fork

https://github.com/vantage6/vantage6Bart van BeusekomJun 10, 2025via ghsa
2 files changed · +31 18
  • vantage6-server/vantage6/server/resource/common/auth_helper.py+5 2 modified
    @@ -204,14 +204,17 @@ def __notify_user_blocked(
         )
         email_sent_recently = user.last_email_failed_login_sent and (
             dt.datetime.now(dt.timezone.utc)
    -        < user.last_email_failed_login_sent
    +        < user.last_email_failed_login_sent.replace(tzinfo=dt.timezone.utc)
             + dt.timedelta(minutes=minutes_between_blocked_emails)
         )
         if email_sent_recently:
             return
     
         # send email
    -    smtp_settings = config.get("smtp", {})
    +    smtp_settings = config.get("smtp")
    +    if not smtp_settings:
    +        log.warning("No SMTP settings found in config - cannot send email!")
    +        return
         email_from = smtp_settings.get("email_from", DEFAULT_EMAIL_FROM_ADDRESS)
         support_email = config.get("support_email", DEFAULT_SUPPORT_EMAIL_ADDRESS)
     
    
  • vantage6-server/vantage6/server/resource/recover.py+26 16 modified
    @@ -142,7 +142,10 @@ def _handle_password_recovery(
             "between_user_emails_minutes",
             DEFAULT_BETWEEN_USER_EMAILS_MINUTES,
         )
    -    smtp_settings = config.get("smtp", {})
    +    smtp_settings = config.get("smtp")
    +    if not smtp_settings:
    +        log.warning("No SMTP settings found in config - cannot send email!")
    +        return
         minutes_token_valid = smtp_settings.get(
             "email_token_validity_minutes", DEFAULT_EMAILED_TOKEN_VALIDITY_MINUTES
         )
    @@ -264,7 +267,7 @@ def post(self):
             if msg:
                 return {"msg": msg}, HTTPStatus.BAD_REQUEST
     
    -        log.info(f"Successfull password reset for '{user.username}'")
    +        log.info("Successful password reset for '%s'", user.username)
             return {"msg": "The password has successfully been reset!"}, HTTPStatus.OK
     
     
    @@ -384,7 +387,7 @@ def post(self):
             user = db.User.get(user_id)
             server_name = self.config.get("server_name", MAIN_VERSION_NAME)
     
    -        log.info(f"Resetting two-factor authentication for {user.username}")
    +        log.info("Resetting two-factor authentication for %s", user.username)
             return create_qr_uri(user, server_name), HTTPStatus.OK
     
     
    @@ -435,15 +438,21 @@ def post(self):
             # check credentials
             user, login_status = user_login(self.config, username, password, self.mail)
             if login_status != HTTPStatus.OK:
    -            log.error(f"Failed attempt to reset 2FA for submitted user '%s'", username)
    +            log.error("Failed attempt to reset 2FA for submitted user '%s'", username)
                 # Note: user_login() returns a dict with an error message if login
                 #       failed as first returned element ('user')
                 return user, login_status
     
    -        log.info(f"2FA reset requested for '{user.username}'")
    +        log.info("2FA reset requested for '%s'", user.username)
     
             # generate a token that can reset their password
    -        smtp_settings = self.config.get("smtp", {})
    +        smtp_settings = self.config.get("smtp")
    +        if not smtp_settings:
    +            log.warning("No SMTP settings found in config - cannot send email!")
    +            return {
    +                "msg": "No mailserver settings found in server configuration - cannot "
    +                "send you an email to reset your 2FA! Contact your administrator."
    +            }, HTTPStatus.BAD_REQUEST
             minutes_token_valid = smtp_settings.get(
                 "email_token_validity_minutes", DEFAULT_EMAILED_TOKEN_VALIDITY_MINUTES
             )
    @@ -476,7 +485,8 @@ def post(self):
             log.info("2FA reset request email sent for '%s'", user.username)
     
             return {
    -            "msg": "You should have received an email that will allow you to reset your 2FA."
    +            "msg": "You should have received an email that will allow you to reset your"
    +            " 2FA."
             }, login_status
     
     
    @@ -529,15 +539,15 @@ def patch(self):
             old_password = body.get("current_password")
             new_password = body.get("new_password")
     
    -        user = g.user
    -        log.info(f"Changing password for user {user.id}")
    +        user: User = g.user
    +        log.info("Changing password for user %s", user.id)
     
             # check if the old password is correct
    -        pw_correct = user.check_password(old_password)
    -        if not pw_correct:
    -            return {
    -                "msg": "Your current password is not correct!"
    -            }, HTTPStatus.UNAUTHORIZED
    +        user_or_error, code = user_login(
    +            self.config, user.username, old_password, self.mail
    +        )
    +        if code != HTTPStatus.OK:
    +            return user_or_error, code
     
             if old_password == new_password:
                 return {
    @@ -549,7 +559,7 @@ def patch(self):
             if msg:
                 return {"msg": msg}, HTTPStatus.BAD_REQUEST
     
    -        log.info(f"Successful password change for '{user.username}'")
    +        log.info("Successful password change for '%s'", user.username)
             return {"msg": "The password has been changed successfully!"}, HTTPStatus.OK
     
     
    @@ -626,7 +636,7 @@ def post(self):
                 }, HTTPStatus.UNAUTHORIZED
     
             # all good, change API key
    -        log.info(f"Successful API key reset for node {id}")
    +        log.info("Successful API key reset for node %s", id_)
             api_key = generate_apikey()
             node.api_key = api_key
             node.save()
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

4

News mentions

0

No linked articles in our index yet.