Traefik HTTP/3 Backend NTLM Connection Reuse
위협 신호 · CVSS · EPSS · KEV
이론적 심각도 점수
예측 데이터 없음
실측 악용 기록 없음
계획된 패치 주기 내 조치(60일 이내)
CVSS 벡터 · 메트릭
CVSS 벡터 정보 없음
상세 설명
Summary
Traefik's HTTP/3 request path did not initialize the connection-scoped backend transport holder that isolates connection-bound NTLM and Negotiate (Kerberos) authentication on the HTTP/1.1 and HTTP/2 paths. The HTTP/3 entrypoint reuses the HTTPS handler chain and reaches the same backend round-tripper, but its ConnContext never called service.AddTransportOnContext, so kerberosRoundTripper fell back to the shared backend transport instead of a per-frontend-connection pool. On a route served over HTTP/3 to a backend that binds identity to a persistent connection via NTLM or Negotiate, an unrelated HTTP/3 client could be assigned a backend connection already authenticated as a victim and inherit that identity, reading victim-only data and performing actions as the victim without presenting the victim's credentials. Affected deployments require HTTP/3 enabled on the entrypoint, a backend using connection-bound NTLM/Negotiate authentication, and backend keep-alive; deployments using ordinary per-request authentication are not affected.
Patches
For more information
If you have any questions or comments about this advisory, please open an issue.
<details> <summary>Original Description</summary>Traefik HTTP/3 Backend NTLM Connection Reuse
Summary
Traefik's HTTP/3 request path does not initialize the connection-scoped backend transport state that Traefik uses to isolate connection-bound NTLM and Negotiate authentication for HTTP/1.1 and HTTP/2. When a backend keeps authenticated identity on a persistent HTTP/1.1 TCP connection, an unrelated HTTP/3 client can reuse a victim-authenticated backend connection and inherit that backend identity.
In the attached reproduction, the HTTPS/HTTP/1.1 control case behaves correctly and isolates the attacker, but the HTTP/3 case allows a second unauthenticated client to read victim-only data and execute a state-changing request as actor=victim.
Validated target:
- Repository:
traefik/traefik - Commit:
f2d0794417e4d06343e6e7c4722143f5b34bee45 - Validation time:
2026-08-25T06:48:02Z - Commit time:
2026-08-24T08:26:06Z - Patched status: not evaluated
Details
The issue is caused by a protocol-parity gap between the normal TCP HTTP entrypoint path and the HTTP/3 entrypoint path.
For HTTP/1.1 and HTTP/2, Traefik explicitly creates a connection-scoped holder that can later store a dedicated RoundTripper for NTLM or Negotiate:
1// pkg/server/server_entrypoint_tcp.go:691-703 2var connContext multipleConnContext 3connContext.AddConnContextFunc(func(ctx context.Context, c net.Conn) context.Context { 4 // This adds an empty struct in order to store a RoundTripper in the ConnContext in case of Kerberos or NTLM. 5 ctx = service.AddTransportOnContext(ctx) 6 7 if tlsConn, ok := c.(*tls.Conn); ok { 8 if tlsConnWithOptionsName, ok := tlsConn.NetConn().(tcp.TLSConn); ok { 9 return tcp.AddTLSOptionsNameInContext(ctx, tlsConnWithOptionsName.TLSOptionsName)10 }11 }12 13 return ctx14})That helper installs the per-connection holder, and kerberosRoundTripper depends on it. If the holder is absent, it falls back to the shared original backend transport. If NTLM or Negotiate is detected, it stores a dedicated cloned RoundTripper into that holder so future requests stay on the authenticated backend connection:
1// pkg/server/service/transport.go:374-402 2func AddTransportOnContext(ctx context.Context) context.Context { 3 return context.WithValue(ctx, transportKey, &stickyRoundTripper{}) 4} 5 6type kerberosRoundTripper struct { 7 new func() http.RoundTripper 8 OriginalRoundTripper http.RoundTripper 9}10 11func (k *kerberosRoundTripper) RoundTrip(request *http.Request) (*http.Response, error) {12 value, ok := request.Context().Value(transportKey).(*stickyRoundTripper)13 if !ok {14 return k.OriginalRoundTripper.RoundTrip(request)15 }16 17 if value.RoundTripper != nil {18 return value.RoundTripper.RoundTrip(request)19 }20 21 resp, err := k.OriginalRoundTripper.RoundTrip(request)22 23 // If we found that we are authenticating with Kerberos (Negotiate) or NTLM.24 // We put a dedicated roundTripper in the ConnContext.25 // This will stick the next calls to the same connection with the backend.26 if err == nil && containsNTLMorNegotiate(resp.Header.Values("WWW-Authenticate")) {27 value.RoundTripper = k.new()28 }29 return resp, err30}For HTTP/3, the server reuses the normal HTTPS handler chain, but its ConnContext only propagates the TLS options name and does not call service.AddTransportOnContext:
1// pkg/server/server_entrypoint_tcp_http3.go:65-80 2h3.Server = &http3.Server{ 3 Addr: config.GetAddress(), 4 Port: config.HTTP3.AdvertisedPort, 5 Handler: httpsServer.Server.(*http.Server).Handler, 6 TLSConfig: &tls.Config{GetConfigForClient: h3.getTLSConfigForClient}, 7 QUICConfig: &quic.Config{ 8 Allow0RTT: false, 9 },10 ConnContext: func(ctx context.Context, c *quic.Conn) context.Context {11 tlsOptionsName, err := h3.getTLSOptionsName(c)12 if err != nil {13 log.Error().Msgf("Error getting TLS options name for client: %v", err)14 return ctx15 }16 return tcp.AddTLSOptionsNameInContext(ctx, tlsOptionsName)17 },18}This means HTTP/3 requests reach the same reverse-proxy and backend transport logic as HTTPS, but without the connection-scoped transport holder that NTLM and Negotiate isolation relies on.
In practice, the flow is:
- A victim authenticates through Traefik to a backend that binds identity to the backend TCP connection using NTLM or Negotiate.
- Because the HTTP/3 request context does not contain
transportKey,kerberosRoundTripperuses the sharedOriginalRoundTripper. - No frontend-connection-specific dedicated backend pool is installed for that HTTP/3 client.
- A second unrelated HTTP/3 client can be assigned the same backend TCP connection after the victim has authenticated it.
- That second client inherits the victim's backend identity without sending the victim's credentials.
The attached verifier demonstrates both the negative control and the exploit path:
- HTTPS/HTTP/1.1 control case: the attacker uses a separate frontend connection and correctly receives
401 - HTTP/3 exploit case: the attacker uses a separate HTTP/3 client with no
Authorizationheader, readsresource=secret actor=victim, executesaction=transfer actor=victim to=attacker amount=5000, and hits the same backend TCP connection identifier as the victim
PoC
See the reproduction materials at:
https://gist.github.com/OneZ3r0/41da8e8b79ebbe444a94f8a2a3a30895
The gist can also be downloaded as a ZIP archive.
Files included in this gist:
run.shDockerfile.dockerignorego.modgo.sumverify.go
The package is intentionally kept as a single-container reproduction:
run.shbuilds a local image for the pinned target commit- the Dockerfile builds both Traefik and the verifier during image build
- the container runs the verifier directly as its entrypoint
- the verifier starts a synthetic backend, launches Traefik, runs the HTTPS/HTTP/1.1 control case, then runs the HTTP/3 exploit case
Run:
1./run.shrun.sh defaults to the validated commit above. To override it explicitly:
1PRODUCT_COMMIT=f2d0794417e4d06343e6e7c4722143f5b34bee45 ./run.shExpected terminal result:
1REPRODUCED: HTTP/1.1 isolates the authenticated backend connection, but HTTP/3 reuses the victim-authenticated backend connection for a different client and executes an unauthorized state-changing request as the victim.Important observed behavior from the PoC:
- the HTTP/1.1 control case succeeds only if a fresh attacker connection receives
401 - the HTTP/3 exploit case succeeds only if the attacker reads victim-only data without sending
Authorization - the HTTP/3 exploit case succeeds only if the attacker performs
/transfer?to=attacker&amount=5000asactor=victim - the HTTP/3 exploit case succeeds only if the attacker uses the same backend TCP connection identifier as the victim
Environment notes:
- Docker is required
- the build fetches the target Traefik source from GitHub
- no production credentials or external NTLM service are required; the verifier includes a synthetic NTLM-like backend specifically to demonstrate connection-bound identity reuse
Impact
This is a cross-client authorization bypass affecting deployments that expose HTTP/3 routes to backends using connection-bound NTLM or Negotiate authentication with persistent backend connection reuse.
In the verified reproduction, an unauthenticated second client can:
- read victim-only data
- perform a state-changing action as the victim
- reuse a backend TCP connection that has already been authenticated as the victim
Attack prerequisites:
- HTTP/3 enabled on the Traefik entrypoint
- a routed backend using connection-bound NTLM or Negotiate authentication
- backend keep-alive and backend connection reuse enabled
- the attacker can reach the same route as the victim
Deployments using ordinary per-request authentication are not affected by this specific issue.
</details> ---AI 심층 분석
공격 시나리오 · 재현 가능한 PoC 페이로드 · 즉시 적용 가능한 차단 패치를 한 번에 받아 보세요. 보안 운영팀이 그대로 점검·티켓팅에 쓸 수 있는 형태로 정리해 드립니다.
참고 자료 7
링크 내용 불러오는 중…