Skip to main content
CVE Vulnerability Database
Vulnerability Database/CVE-2026-11922

CVE-2026-11922: ZenML Auth Bypass Vulnerability

CVE-2026-11922 is an authentication bypass flaw in ZenML versions 0.57.0 through 0.94.2 that enables attackers to circumvent rate-limiting protections. This article covers the technical details, affected versions, and steps to secure your systems.

Published:

CVE-2026-11922 Overview

CVE-2026-11922 affects zenml-io/zenml versions 0.57.0 through 0.94.2. The vulnerability allows attackers to bypass rate-limiting on the POST /api/v1/login endpoint and self password-change endpoints by rotating the X-Forwarded-For header. The rate limiter keys requests by request.client.host, which is populated from the X-Forwarded-For header when Uvicorn runs with --proxy-headers --forwarded-allow-ips *. This design lets clients control the rate-limiter key, enabling unthrottled credential guessing attacks. The issue is categorized under [CWE-290] Authentication Bypass by Spoofing.

Critical Impact

Attackers can execute unrestricted brute-force attempts against authentication endpoints by rotating a client-controlled header, exposing accounts to credential guessing.

Affected Products

  • zenml-io/zenml version 0.57.0 (lower bound)
  • zenml-io/zenml versions 0.57.0 through 0.94.2
  • Deployments running Uvicorn with --proxy-headers --forwarded-allow-ips *

Discovery Timeline

  • 2026-07-24 - CVE-2026-11922 published to NVD
  • 2026-07-24 - Last updated in NVD database

Technical Details for CVE-2026-11922

Vulnerability Analysis

The ZenML server uses a rate limiter that identifies clients by IP address to throttle authentication attempts. The _get_ipaddr method in src/zenml/zen_server/rate_limit.py reads the X-Forwarded-For header before falling back to request.client.host. When Uvicorn is launched with --proxy-headers --forwarded-allow-ips *, the ASGI layer already trusts and rewrites request.client.host from X-Forwarded-For. Both code paths end up honoring an attacker-controlled header.

An attacker can rotate the X-Forwarded-For value on every request, producing a new rate-limit key each time. The limiter never accumulates failed attempts against a single identifier, so throttling never triggers on the POST /api/v1/login endpoint or the self password-change endpoints.

Root Cause

The root cause is trust in a client-controlled header for security-critical keying. The permissive --forwarded-allow-ips * configuration accepts X-Forwarded-For from any source, and the rate limiter compounds the problem by re-reading the header directly. Neither layer validates that the request originated from a trusted reverse proxy.

Attack Vector

The attack is remote, requires no authentication, and no user interaction. An attacker sends repeated login attempts to POST /api/v1/login, changing the X-Forwarded-For header on each request to defeat the per-IP counter.

python
    def _get_ipaddr(self, request: Request) -> str:
        """Returns the IP address for the current request.

-        Based on the X-Forwarded-For headers or client information.
+        Based on the client information provided by the ASGI server.

         Args:
             request: The request object.

         Returns:
             The ip address for the current request (or 127.0.0.1 if none found).
        """
-        if "X_FORWARDED_FOR" in request.headers:
-            return request.headers["X_FORWARDED_FOR"]
-        else:
-            if not request.client or not request.client.host:
-                return "127.0.0.1"
+        if not request.client or not request.client.host:
+            return "127.0.0.1"

-            return request.client.host
+        return request.client.host

Source: GitHub Commit 8a2214b

The patch removes direct reading of X_FORWARDED_FOR from request headers and relies on client information supplied by the ASGI server, with proxy trust configured explicitly at deployment time.

Detection Methods for CVE-2026-11922

Indicators of Compromise

  • High volume of POST /api/v1/login requests from the same TCP source with varying X-Forwarded-For values.
  • Successive failed authentication events for one or many usernames without corresponding rate-limit rejections.
  • Requests to self password-change endpoints containing spoofed or private-range X-Forwarded-For addresses.

Detection Strategies

  • Correlate authentication failures by source TCP address rather than by the X-Forwarded-For header value.
  • Flag any client that presents more than a threshold number of distinct X-Forwarded-For values within a short window.
  • Alert when ZenML login failures spike without matching HTTP 429 rate-limit responses.

Monitoring Recommendations

  • Ingest ZenML server access logs and Uvicorn proxy logs into a centralized analytics pipeline.
  • Monitor the ratio of HTTP 401 to HTTP 429 responses on /api/v1/login; a shrinking 429 count indicates bypass.
  • Track the diversity of X-Forwarded-For header values per socket peer to detect header rotation.

How to Mitigate CVE-2026-11922

Immediate Actions Required

  • Upgrade zenml-io/zenml to a release later than 0.94.2 that includes commit 8a2214b.
  • Remove --forwarded-allow-ips * from Uvicorn startup and restrict trusted proxy IPs to known reverse-proxy addresses.
  • Enforce rate limiting at the reverse proxy or ingress layer as a defense-in-depth control.
  • Review authentication logs for evidence of brute-force activity predating the patch.

Patch Information

The fix is delivered in GitHub Commit 8a2214b. It removes the direct read of X_FORWARDED_FOR in rate_limit.py and updates Helm templates to align with the corrected trust model. Additional context is available on the Huntr Bounty Listing.

Workarounds

  • Configure Uvicorn with an explicit allow-list of reverse-proxy IPs instead of *.
  • Apply IP-based throttling at NGINX, HAProxy, or an API gateway using the real source IP.
  • Require multi-factor authentication on ZenML accounts to reduce the value of credential guessing.
bash
# Configuration example - restrict trusted proxies at the ASGI layer
uvicorn zenml.zen_server.zen_server_api:app \
  --host 0.0.0.0 \
  --port 8080 \
  --proxy-headers \
  --forwarded-allow-ips "10.0.0.5,10.0.0.6"

Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

Default Legacy - Prefooter | Experience the World’s Most Advanced Cybersecurity Platform

Experience the Most Advanced Cybersecurity Platform

See how the world’s most intelligent, autonomous cybersecurity platform can protect your organization today and into the future.