Authentication Bypass by Primary Weakness in octoprint/octoprint
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.
| Package | Affected versions | Patched versions |
|---|---|---|
OctoPrintPyPI | <= 1.7.3 | — |
Affected products
2- octoprint/octoprint/octoprintv5Range: unspecified
Patches
182c892ba40b3🔒️ Rate limit failed login attempts
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- github.com/advisories/GHSA-5w5x-q9p5-9qg3ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2022-2822ghsaADVISORY
- github.com/octoprint/octoprint/commit/82c892ba40b3741d1b7711d949e56af64f5bc2deghsax_refsource_MISCWEB
- huntr.dev/bounties/6369f355-e6ef-4469-af75-0f6ff00cde3dghsax_refsource_CONFIRMWEB
News mentions
0No linked articles in our index yet.