VYPR
Low severityNVD Advisory· Published Aug 15, 2022· Updated Aug 3, 2024

Authentication Bypass by Primary Weakness in octoprint/octoprint

CVE-2022-2822

Description

An attacker can freely brute force username and password and can takeover any account. An attacker could easily guess user passwords and gain access to user and administrative accounts.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

OctoPrint lacked rate limiting on login, allowing attackers to brute-force credentials and take over any account.

Root

Cause CVE-2022-2822 describes a missing rate-limiting mechanism on the login endpoint in OctoPrint, a popular 3D printer management web interface. The application did not restrict the number of failed login attempts, enabling an attacker to systematically guess usernames and passwords [1][2].

Exploitation

The vulnerability is exploitable over the network with no special privileges required. An attacker can send repeated login requests to the /api/login route without any throttling, making brute-force attacks highly effective. The commit that fixes the issue introduces Flask-Limiter and applies specific rate limits (e.g., 3 attempts per minute, 5 per 10 minutes, 10 per hour) with a deduction mechanism tied to HTTP 403 responses [1].

Impact

Successful exploitation allows an attacker to gain unauthorized access to any user or administrative account. Given OctoPrint's role in controlling 3D printers, this could lead to physical damage, data theft, or disruption of service [2][4].

Mitigation

The vulnerability was reported via huntr.dev and patched in commit `82c892b` [1][4]. Users should update to a version containing this fix. No workaround is available other than applying the patch.

AI Insight generated on May 21, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
OctoPrintPyPI
<= 1.7.3

Affected products

2
  • ghsa-coords
    Range: <= 1.7.3
  • octoprint/octoprint/octoprintv5
    Range: unspecified

Patches

1
82c892ba40b3

🔒️ Rate limit failed login attempts

https://github.com/octoprint/octoprintGina HäußgeAug 15, 2022via ghsa
7 files changed · +31 7
  • setup.py+1 0 modified
    @@ -43,6 +43,7 @@
         "Flask-Assets>=2.0,<3",
         "Flask-Babel>=2.0,<3",
         "Flask-Login>=0.6,<0.7",  # breaking changes can happen on minor version increases
    +    "Flask-Limiter>=2.6,<3",
         "flask>=2.2,<2.3",  # breaking changes can happen on minor version increases (with deprecation warnings)
         "frozendict>=2.0,<3",
         "future>=0.18.2,<1",  # not really needed anymore, but leaving in for py2/3 compat plugins
    
  • src/octoprint/server/api/__init__.py+5 0 modified
    @@ -281,6 +281,11 @@ def serverStatus():
     
     
     @api.route("/login", methods=["POST"])
    +@octoprint.server.limiter.limit(
    +    "3/minute;5/10 minutes;10/hour",
    +    deduct_when=lambda response: response.status_code == 403,
    +    error_message="You have made too many failed login attempts. Please try again later.",
    +)
     def login():
         data = request.get_json()
         if not data:
    
  • src/octoprint/server/__init__.py+10 0 modified
    @@ -70,6 +70,7 @@
     
     assets = None
     babel = None
    +limiter = None
     debug = False
     safe_mode = False
     
    @@ -1338,6 +1339,8 @@ def log_heartbeat():
             timer.start()
     
         def _setup_app(self, app):
    +        global limiter
    +
             from octoprint.server.util.flask import (
                 OctoPrintFlaskRequest,
                 OctoPrintFlaskResponse,
    @@ -1433,6 +1436,13 @@ def after_request(response):
     
             MarkdownFilter(app)
     
    +        from flask_limiter import Limiter
    +        from flask_limiter.util import get_remote_address
    +
    +        app.config["RATELIMIT_STRATEGY"] = "fixed-window-elastic-expiry"
    +
    +        limiter = Limiter(app, key_func=get_remote_address)
    +
         def _setup_i18n(self, app):
             global babel
             global LOCALES
    
  • src/octoprint/static/css/login.css+1 1 modified
    @@ -1 +1 @@
    -body{padding-top:40px;padding-bottom:40px;background-color:#f5f5f5}.form-signin{max-width:300px;padding:19px 29px 29px;margin:0 auto 20px;background-color:#fff;border:1px solid #e5e5e5;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px;-webkit-box-shadow:0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:0 1px 2px rgba(0,0,0,0.05);box-shadow:0 1px 2px rgba(0,0,0,0.05)}.form-signin .form-signin-heading,.form-signin .checkbox{margin-bottom:10px}.form-signin input[type="text"],.form-signin input[type="password"]{font-size:16px;height:auto;margin-bottom:15px;padding:7px 9px}#login-error,#login-offline{display:none}#login-error.in,#login-offline.in{display:block}#login-overlay{position:fixed;top:0;left:0;width:100%;height:100%;z-index:10000;display:none}#login-overlay.in{display:block}#login-overlay .background{position:fixed;top:0;left:0;width:100%;height:100%;background-color:#000000;filter:alpha(opacity=50);-moz-opacity:.5;-khtml-opacity:.5;opacity:.5}#login-overlay .wrapper{position:absolute;top:0;bottom:0;left:0;right:0}#login-overlay .wrapper .outer{display:table;width:100%;height:100%}#login-overlay .wrapper .outer .inner{display:table-cell;vertical-align:middle}#login-overlay .wrapper .outer .inner .content{text-align:center;color:white}#noscript{position:fixed;top:0;left:0;width:100%;height:100%;background-color:#ffffff;z-index:12310}#noscript .wrapper{position:absolute;top:0;bottom:0;left:0;right:0}#noscript .wrapper .outer{display:table;width:100%;height:100%}#noscript .wrapper .outer .inner{display:table-cell;vertical-align:middle}#noscript .wrapper .outer .inner .content{text-align:center}
    \ No newline at end of file
    +body{padding-top:40px;padding-bottom:40px;background-color:#f5f5f5}.form-signin{max-width:300px;padding:19px 29px 29px;margin:0 auto 20px;background-color:#fff;border:1px solid #e5e5e5;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px;-webkit-box-shadow:0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:0 1px 2px rgba(0,0,0,0.05);box-shadow:0 1px 2px rgba(0,0,0,0.05)}.form-signin .form-signin-heading,.form-signin .checkbox{margin-bottom:10px}.form-signin input[type="text"],.form-signin input[type="password"]{font-size:16px;height:auto;margin-bottom:15px;padding:7px 9px}#login-error-credentials,#login-error-rate,#login-offline{display:none}#login-error-credentials.in,#login-error-rate.in,#login-offline.in{display:block}#login-overlay{position:fixed;top:0;left:0;width:100%;height:100%;z-index:10000;display:none}#login-overlay.in{display:block}#login-overlay .background{position:fixed;top:0;left:0;width:100%;height:100%;background-color:#000000;filter:alpha(opacity=50);-moz-opacity:.5;-khtml-opacity:.5;opacity:.5}#login-overlay .wrapper{position:absolute;top:0;bottom:0;left:0;right:0}#login-overlay .wrapper .outer{display:table;width:100%;height:100%}#login-overlay .wrapper .outer .inner{display:table-cell;vertical-align:middle}#login-overlay .wrapper .outer .inner .content{text-align:center;color:white}#noscript{position:fixed;top:0;left:0;width:100%;height:100%;background-color:#ffffff;z-index:12310}#noscript .wrapper{position:absolute;top:0;bottom:0;left:0;right:0}#noscript .wrapper .outer{display:table;width:100%;height:100%}#noscript .wrapper .outer .inner{display:table-cell;vertical-align:middle}#noscript .wrapper .outer .inner .content{text-align:center}
    \ No newline at end of file
    
  • src/octoprint/static/js/login/login.js+10 4 modified
    @@ -11,7 +11,8 @@ $(function () {
         };
     
         var overlayElement = $("#login-overlay");
    -    var errorElement = $("#login-error");
    +    var errorCredentialsElement = $("#login-error-credentials");
    +    var errorRateElement = $("#login-error-rate");
         var offlineElement = $("#login-offline");
         var buttonElement = $("#login-button");
         var reconnectElement = $("#login-reconnect");
    @@ -28,15 +29,16 @@ $(function () {
             var remember = rememberElement.prop("checked");
     
             overlayElement.addClass("in");
    -        errorElement.removeClass("in");
    +        errorCredentialsElement.removeClass("in");
    +        errorRateElement.removeClass("in");
     
             OctoPrint.browser
                 .login(username, password, remember)
                 .done(() => {
                     ignoreDisconnect = true;
                     window.location.href = REDIRECT_URL;
                 })
    -            .fail(() => {
    +            .fail((xhr) => {
                     usernameElement.val(USER_ID);
                     passwordElement.val("");
     
    @@ -47,7 +49,11 @@ $(function () {
                     }
     
                     overlayElement.removeClass("in");
    -                errorElement.addClass("in");
    +                if (xhr.status === 429) {
    +                    errorRateElement.addClass("in");
    +                } else {
    +                    errorCredentialsElement.addClass("in");
    +                }
                 });
     
             return false;
    
  • src/octoprint/static/less/login.less+2 1 modified
    @@ -31,7 +31,8 @@ body {
       }
     }
     
    -#login-error,
    +#login-error-credentials,
    +#login-error-rate,
     #login-offline {
       display: none;
     
    
  • src/octoprint/templates/login.jinja2+2 1 modified
    @@ -65,7 +65,8 @@
             <form class="form-signin">
                 <h2 class="form-signin-heading" data-test-id="login-title">{{ _('Please log in') }}</h2>
     
    -            <div id="login-error" class="alert alert-error" data-test-id="login-error">{{ _('Incorrect username or password. Hint: Both are case sensitive!') }}</div>
    +            <div id="login-error-credentials" class="alert alert-error" data-test-id="login-error">{{ _('Incorrect username or password. Hint: Both are case sensitive!') }}</div>
    +            <div id="login-error-rate" class="alert alert-error" data-test-id="login-error-rate">{{ _('You have made too many failed login attempts. Please try again later.') }}</div>
                 <div id="login-offline" class="alert alert-error">{{ _('Server is currently offline.') }} <a id="login-reconnect" href="javascript:void(0)">{{ _('Reconnect...') }}</a></div>
     
                 {% if user_id %}<p>
    

Vulnerability mechanics

Generated 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.