Moderate severityNVD Advisory· Published Mar 1, 2023· Updated Mar 7, 2025
Observable Response Discrepancy in vantage6
CVE-2022-39228
Description
vantage6 is a privacy preserving federated learning infrastructure for secure insight exchange. vantage6 does not inform the user of wrong username/password combination if the username actually exists. This is an attempt to prevent bots from obtaining usernames. However, if a wrong password is entered a number of times, the user account is blocked temporarily. This issue has been fixed in version 3.8.0.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
vantage6PyPI | < 3.8.0 | 3.8.0 |
Affected products
1Patches
1ab4381c35d24Merge pull request from GHSA-36gx-9q6h-g429
6 files changed · +372 −157
vantage6-server/vantage6/server/mail_service.py+1 −1 modified@@ -20,7 +20,7 @@ def send_async_email(self, app, msg): with app.app_context(): try: self.mail.send(msg) - except ConnectionRefusedError as e: + except Exception as e: log.error("Mailserver error!") log.debug(e)
vantage6-server/vantage6/server/model/user.py+1 −5 modified@@ -130,11 +130,7 @@ def is_blocked(self, max_failed_attempts: int, if has_max_attempts and td_last_login < td_max_blocked: minutes_remaining = \ (td_max_blocked - td_last_login).seconds // 60 + 1 - return True, ( - f"Your account is blocked for the next {minutes_remaining} " - "minutes due to failed login attempts. Please wait or " - "reactivate your account via email." - ) + return True, minutes_remaining else: return False, None
vantage6-server/vantage6/server/resource/token.py+59 −4 modified@@ -2,13 +2,12 @@ """ Resources below '/<api_base>/token' """ -from __future__ import print_function, unicode_literals - import logging +import datetime as dt import pyotp from typing import Union -from flask import request, g +from flask import request, g, render_template from flask_jwt_extended import ( jwt_required, create_access_token, @@ -166,6 +165,62 @@ def post(self): log.info(f"Succesfull login from {username}") return ret, HTTPStatus.OK, {'jwt-token': token} + def user_login(self, username: str, password: str) -> Union[dict, db.User]: + """Returns user a message in case of failed login attempt.""" + log.info(f"Trying to login '{username}'") + failed_login_msg = "Failed to login" + if db.User.username_exists(username): + user = db.User.get_by_username(username) + pw_policy = self.config.get('password_policy', {}) + max_failed_attempts = pw_policy.get('max_failed_attempts', 5) + inactivation_time = pw_policy.get('inactivation_minutes', 15) + + is_blocked, min_rem = user.is_blocked(max_failed_attempts, + inactivation_time) + if is_blocked: + self.notify_user_blocked(user, max_failed_attempts, min_rem) + return {"msg": failed_login_msg}, HTTPStatus.UNAUTHORIZED + elif user.check_password(password): + user.failed_login_attempts = 0 + user.save() + return user, HTTPStatus.OK + else: + # update the number of failed login attempts + user.failed_login_attempts = 1 \ + if ( + not user.failed_login_attempts or + user.failed_login_attempts >= max_failed_attempts + ) else user.failed_login_attempts + 1 + user.last_login_attempt = dt.datetime.now() + user.save() + + return {"msg": failed_login_msg}, HTTPStatus.UNAUTHORIZED + + def notify_user_blocked(self, user: db.User, max_n_attempts: int, + min_rem: int) -> None: + """Sends an email to the user when his or her account is locked""" + if not user.email: + log.warning(f'User {user.username} is locked, but does not have' + 'an email registered. So no message has been sent.') + + log.info(f'User {user.username} is locked') + + template_vars = {'firstname': user.firstname, + 'number_of_allowed_attempts': max_n_attempts, + 'ip': request.access_route[-1], + 'time': dt.datetime.now(dt.timezone.utc), + 'time_remaining': min_rem} + + self.mail.send_email( + "Your account has been temporary suspended", + sender="support@vantage6.ai", + recipients=[user.email], + text_body=render_template("mail/blocked_account.txt", + **template_vars), + html_body=render_template("mail/blocked_account.html", + **template_vars) + ) + @staticmethod def validate_2fa_token(user: User, mfa_code: Union[int, str]) -> bool: """ @@ -197,7 +252,7 @@ def post(self): --- description: >- Allows node to sign in using a unique API key. If the login is - successful this returns a dictionairy with access and refresh tokens + successful this returns a dictionary with access and refresh tokens for the node as well as a node_url and a refresh_url. requestBody:
vantage6-server/vantage6/server/templates/mail/blocked_account.html+157 −0 added@@ -0,0 +1,157 @@ +<!-- + HTML Email Starter Kit + Documentation: https://github.com/timothylong/html-email-starter-kit +--> +<!DOCTYPE html> +<html> + <head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width" initial-scale="1"> + <!--[if !mso]> + <meta http-equiv="X-UA-Compatible" content="IE=edge"> + <![endif]--> + <meta name="x-apple-disable-message-reformatting"> + <title></title> + <!--[if mso]> + <style> + * { font-family: sans-serif !important; } + </style> + <![endif]--> + <!--[if !mso]><!--> + <!-- Insert font reference, e.g. <link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,700" rel="stylesheet"> --> + <!--<![endif]--> + <style> + *, + *:after, + *:before { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + } + * { + -ms-text-size-adjust: 100%; + -webkit-text-size-adjust: 100%; + } + html, + body, + .document { + font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; + font-size: 1.05em; + width: 100% !important; + height: 100% !important; + margin: 0; + padding: 0; + } + body { + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + text-rendering: optimizeLegibility; + } + div[style*="margin: 16px 0"] { + margin: 0 !important; + } + table, + td { + mso-table-lspace: 0pt; + mso-table-rspace: 0pt; + } + table { + border-spacing: 0; + border-collapse: collapse; + table-layout: fixed; + margin: 0 auto; + } + img { + -ms-interpolation-mode: bicubic; + max-width: 100%; + border: 0; + } + *[x-apple-data-detectors] { + color: inherit !important; + text-decoration: none !important; + } + .x-gmail-data-detectors, + .x-gmail-data-detectors *, + .aBn { + border-bottom: 0 !important; + cursor: default !important; + } + .btn { + -webkit-transition: all 200ms ease; + transition: all 200ms ease; + } + .btn:hover { + background-color: dodgerblue; + } + @media screen and (max-width: 750px) { + .container { + width: 100%; + margin: auto; + } + .stack { + display: block; + width: 100%; + max-width: 100%; + } + } + </style> + </head> + <body> + <div style="display: none; max-height: 0px; overflow: hidden;"> + <!-- Preheader message here --> + </div> + <div style="display: none; max-height: 0px; overflow: hidden;"> ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ </div> + <table style="background-color: #fff;" role="presentation" aria-hidden="true" cellspacing="0" cellpadding="0" border="0" align="center" class="document"> + <tr> + <td valign="top"> + <table role="presentation" aria-hidden="true" cellspacing="0" cellpadding="0" border="0" align="center" width="750" class="container"> + <tr> + <td> + <table role="presentation" aria-hidden="true" cellspacing="0" cellpadding="0" border="0" align="center" width="100%"> + <tr> + <td width=250></td> + <td style="padding:20px;"> + <img src="https://raw.githubusercontent.com/IKNL/guidelines/master/resources/logos/vantage6.png" width=250> + </td> + <td width=250></td> + </tr> + <tr style="background-color:#f9f9f9;" width="100%"> + <td colspan=3 style="padding:40px; color:#0f497b;"> + + Dear <strong>{{firstname}}</strong>, + + <p> + After {{number_of_allowed_attempts}} failed login attempts your user account has been blocked.<br/> + <ul> + <li>Source IP: {{ip}}</li> + <li>Last attempt at: {{time}} (UTC)</li> + </ul> + </p> + <p> + Your account will be unlocked in {{time_remaining}} minutes. Please wait or reactivate your account via email. + </p> + <p>Sincerely, <br/>vantage6 Support Team</p> + </td> + </tr> + </table> + </td> + </tr> + </table> + <table role="presentation" aria-hidden="true" cellspacing="0" cellpadding="0" border="0" align="center" width="750" class="container"> + <tr style="font-size: .6em;"> + <td style="padding-top:20px; padding-bottom: 20px;"> + <a style="color:#0f497b; font-weight: bold;" href="https://vantage6.ai">vantage6.ai</a> + </td> + <td style="padding-top:20px; padding-bottom: 20px;text-align: center;"> + <a style="color:#0f497b; font-weight: bold;" href="https://discord.gg/yAyFf6Y">Join our Discord channel</a> + </td > + <td style="padding-top:20px; padding-bottom: 20px;text-align: right;"> + <a style="color:#0f497b; font-weight: bold;" href="mailto:support@vantage6.ai">support@vantage6.ai</a> + </td> + </tr> + </table> + </td> + </tr> + </table> + </body> +</html> \ No newline at end of file
vantage6-server/vantage6/server/templates/mail/blocked_account.txt+10 −0 added@@ -0,0 +1,10 @@ +Dear {{firstname}}, + +After {{number_of_allowed_attempts}} failed login attempts your user account has been blocked. + +Source IP: {{ip}} +Last attempt at: {{time}} (UTC) + +Your account will be unlocked in {{time_remaining}} minutes. Please wait or reactivate your account via email. + +Sincerely, vantage6 Support Team \ No newline at end of file
vantage6-server/vantage6/server/templates/mail/reset_token.html+144 −147 modified@@ -4,155 +4,152 @@ --> <!DOCTYPE html> <html> -<head> - <meta charset="UTF-8"> - <meta name="viewport" content="width=device-width" initial-scale="1"> - <!--[if !mso]> - <meta http-equiv="X-UA-Compatible" content="IE=edge"> - <![endif]--> - <meta name="x-apple-disable-message-reformatting"> - <title></title> - <!--[if mso]> + <head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width" initial-scale="1"> + <!--[if !mso]> + <meta http-equiv="X-UA-Compatible" content="IE=edge"> + <![endif]--> + <meta name="x-apple-disable-message-reformatting"> + <title></title> + <!--[if mso]> + <style> + * { font-family: sans-serif !important; } + </style> + <![endif]--> + <!--[if !mso]><!--> + <!-- Insert font reference, e.g. <link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,700" rel="stylesheet"> --> + <!--<![endif]--> <style> - * { font-family: sans-serif !important; } - </style> - <![endif]--> - <!--[if !mso]><!--> - <!-- Insert font reference, e.g. <link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,700" rel="stylesheet"> --> - <!--<![endif]--> - <style> - *, - *:after, - *:before { - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; - } - * { - -ms-text-size-adjust: 100%; - -webkit-text-size-adjust: 100%; - } - html, - body, - .document { - font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; - font-size: 1.05em; - width: 100% !important; - height: 100% !important; - margin: 0; - padding: 0; - } - body { - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; - text-rendering: optimizeLegibility; - } - div[style*="margin: 16px 0"] { - margin: 0 !important; - } - table, - td { - mso-table-lspace: 0pt; - mso-table-rspace: 0pt; - } - table { - border-spacing: 0; - border-collapse: collapse; - table-layout: fixed; - margin: 0 auto; - } - img { - -ms-interpolation-mode: bicubic; - max-width: 100%; - border: 0; - } - *[x-apple-data-detectors] { - color: inherit !important; - text-decoration: none !important; - } - .x-gmail-data-detectors, - .x-gmail-data-detectors *, - .aBn { - border-bottom: 0 !important; - cursor: default !important; - } - .btn { - -webkit-transition: all 200ms ease; - transition: all 200ms ease; - } - .btn:hover { - background-color: dodgerblue; - } - @media screen and (max-width: 750px) { - .container { - width: 100%; - margin: auto; + *, + *:after, + *:before { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + } + * { + -ms-text-size-adjust: 100%; + -webkit-text-size-adjust: 100%; + } + html, + body, + .document { + font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; + font-size: 1.05em; + width: 100% !important; + height: 100% !important; + margin: 0; + padding: 0; + } + body { + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + text-rendering: optimizeLegibility; + } + div[style*="margin: 16px 0"] { + margin: 0 !important; + } + table, + td { + mso-table-lspace: 0pt; + mso-table-rspace: 0pt; + } + table { + border-spacing: 0; + border-collapse: collapse; + table-layout: fixed; + margin: 0 auto; } - .stack { - display: block; - width: 100%; + img { + -ms-interpolation-mode: bicubic; max-width: 100%; + border: 0; } - } - /* #11B5E9; light blue - #0f497b; Dark blue - #f9f9f9; White - #f9ac23; Orange - #aabfce; Light grey? */ - </style> -</head> -<body> - <div style="display: none; max-height: 0px; overflow: hidden;"> - <!-- Preheader message here --> - </div> - <div style="display: none; max-height: 0px; overflow: hidden;"> ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ </div> - <table style="background-color: #fff;" role="presentation" aria-hidden="true" cellspacing="0" cellpadding="0" border="0" align="center" class="document"> - <tr> - <td valign="top"> - <table role="presentation" aria-hidden="true" cellspacing="0" cellpadding="0" border="0" align="center" width="750" class="container"> - <tr> - <td> - <table role="presentation" aria-hidden="true" cellspacing="0" cellpadding="0" border="0" align="center" width="100%"> - <tr> - <td width=250></td> - <td style="padding:20px;"> - <img src="https://raw.githubusercontent.com/IKNL/guidelines/master/resources/logos/vantage6.png" width=250> - </td> - <td width=250></td> - </tr> - <tr style="background-color:#f9f9f9;" width="100%"> - <td colspan=3 style="padding:40px; color:#0f497b;"> - - Dear <strong>{{firstname}}</strong>, - - <p>To reset your {{reset_type}} use the following token:</p> - <p style="font-family: 'Courier New', Courier, monospace; font-size: 12px; overflow-wrap:break-word;padding:10px; color: white; background-color: #0f497b;"> - {{token}} - </p> - <p>If you have not requested a {{reset_type}} reset, {{what_to_do}}.</p> - <p>Sincerely, <br/>vantage6 Support Team</p> - </td> - </tr> - </table> - </td> - </tr> - </table> - <table role="presentation" aria-hidden="true" cellspacing="0" cellpadding="0" border="0" align="center" width="750" class="container"> - <tr style="font-size: .6em;"> - <td style="padding-top:20px; padding-bottom: 20px;"> - <a style="color:#0f497b; font-weight: bold;" href="https://vantage6.ai">vantage6.ai</a> - </td> - <td style="padding-top:20px; padding-bottom: 20px;text-align: center;"> - <a style="color:#0f497b; font-weight: bold;" href="https://discord.gg/yAyFf6Y">Join our Discord channel</a> - </td > - <td style="padding-top:20px; padding-bottom: 20px;text-align: right;"> - <a style="color:#0f497b; font-weight: bold;" href="mailto:{{support_email}}">{{support_email}}</a> - </td> - </tr> - - </table> - </td> - </tr> - </table> -</body> + *[x-apple-data-detectors] { + color: inherit !important; + text-decoration: none !important; + } + .x-gmail-data-detectors, + .x-gmail-data-detectors *, + .aBn { + border-bottom: 0 !important; + cursor: default !important; + } + .btn { + -webkit-transition: all 200ms ease; + transition: all 200ms ease; + } + .btn:hover { + background-color: dodgerblue; + } + @media screen and (max-width: 750px) { + .container { + width: 100%; + margin: auto; + } + .stack { + display: block; + width: 100%; + max-width: 100%; + } + } + /* #11B5E9; light blue + #0f497b; Dark blue + #f9f9f9; White + #f9ac23; Orange + #aabfce; Light grey? */ + </style> + </head> + <body> + <div style="display: none; max-height: 0px; overflow: hidden;"> + <!-- Preheader message here --> + </div> + <div style="display: none; max-height: 0px; overflow: hidden;"> ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ </div> + <table style="background-color: #fff;" role="presentation" aria-hidden="true" cellspacing="0" cellpadding="0" border="0" align="center" class="document"> + <tr> + <td valign="top"> + <table role="presentation" aria-hidden="true" cellspacing="0" cellpadding="0" border="0" align="center" width="750" class="container"> + <tr> + <td> + <table role="presentation" aria-hidden="true" cellspacing="0" cellpadding="0" border="0" align="center" width="100%"> + <tr> + <td width=250></td> + <td style="padding:20px;"> + <img src="https://raw.githubusercontent.com/IKNL/guidelines/master/resources/logos/vantage6.png" width=250> + </td> + <td width=250></td> + </tr> + <tr style="background-color:#f9f9f9;" width="100%"> + <td colspan=3 style="padding:40px; color:#0f497b;"> + Dear <strong>{{firstname}}</strong>, + <p>To reset your {{reset_type}} use the following token:</p> + <p style="font-family: 'Courier New', Courier, monospace; font-size: 12px; overflow-wrap:break-word;padding:10px; color: white; background-color: #0f497b;"> + {{token}} + </p> + <p>If you have not requested a {{reset_type}} reset, {{what_to_do}}.</p> + <p>Sincerely, <br/>vantage6 Support Team</p> + </td> + </tr> + </table> + </td> + </tr> + </table> + <table role="presentation" aria-hidden="true" cellspacing="0" cellpadding="0" border="0" align="center" width="750" class="container"> + <tr style="font-size: .6em;"> + <td style="padding-top:20px; padding-bottom: 20px;"> + <a style="color:#0f497b; font-weight: bold;" href="https://vantage6.ai">vantage6.ai</a> + </td> + <td style="padding-top:20px; padding-bottom: 20px;text-align: center;"> + <a style="color:#0f497b; font-weight: bold;" href="https://discord.gg/yAyFf6Y">Join our Discord channel</a> + </td > + <td style="padding-top:20px; padding-bottom: 20px;text-align: right;"> + <a style="color:#0f497b; font-weight: bold;" href="mailto:{{support_email}}">{{support_email}}</a> + </td> + </tr> + </table> + </td> + </tr> + </table> + </body> </html> \ No newline at end of file
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
7- github.com/advisories/GHSA-36gx-9q6h-g429ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2022-39228ghsaADVISORY
- github.com/pypa/advisory-database/tree/main/vulns/vantage6/PYSEC-2023-52.yamlghsaWEB
- github.com/vantage6/vantage6/commit/ab4381c35d24add06f75d5a8a284321f7a340bd2ghsax_refsource_MISCWEB
- github.com/vantage6/vantage6/issues/59ghsax_refsource_MISCWEB
- github.com/vantage6/vantage6/pull/281ghsax_refsource_MISCWEB
- github.com/vantage6/vantage6/security/advisories/GHSA-36gx-9q6h-g429ghsax_refsource_CONFIRMWEB
News mentions
0No linked articles in our index yet.